mirror of
https://github.com/Aviortheking/codestats-skyline.git
synced 2025-04-22 02:02:09 +00:00
feat: Add support to specify year
+ Did some cleanup for the whole code Signed-off-by: Avior <f.bouillon@aptatio.com>
This commit is contained in:
parent
abfd6d8445
commit
9a8cb6dff4
12
.editorconfig
Normal file
12
.editorconfig
Normal file
@ -0,0 +1,12 @@
|
||||
# EditorConfig is awesome: https://EditorConfig.org
|
||||
|
||||
# top-most EditorConfig file
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -1 +1,2 @@
|
||||
node_modules
|
||||
node_modules
|
||||
dist/
|
||||
|
129
3d.scad
129
3d.scad
@ -1,5 +1,6 @@
|
||||
// -- TO CHANGE START -- //
|
||||
// do not chang it yourself
|
||||
/*- TO CHANGE START */
|
||||
|
||||
// Number of XPs in a day for each months
|
||||
jan = [6433, 12574, 12013, 14896, 6242, 12306, 11732, 22842, 25780, 7375, 1030, 2676, 8857, 7948, 11429, 21389, 13025, 5335, 8942, 4457, 14171, 9951, 10766, 7780, 29865, 24567, 7702, 24382, 17697, 12100, 16698];
|
||||
feb = [21093, 2887, 10773, 10345, 5897, 6255, 20849, 17071, 17168, 8504, 27323, 33772, 16624, 16915, 14535, 10766, 24339, 14177, 24986, 13760, 47418, 59739, 19918, 12892, 20671, 13143, 9618, 9389, 0, 0];
|
||||
mar = [23643, 29897, 23874, 26867, 20092, 8363, 6073, 10425, 26663, 111087, 30647, 19681, 19344, 15507, 17636, 12541, 7369, 17568, 17077, 15472, 9506, 6338, 8851, 21544, 24243, 7023, 5401, 7157, 20584, 23584, 16505];
|
||||
@ -12,76 +13,108 @@ sep = [8156, 14355, 7410, 7958, 5638, 1974, 7015, 3924, 559, 8996, 19943, 28816,
|
||||
oct = [7109, 11067, 13580, 24341, 10774, 11742, 19973, 23626, 30664, 9417, 16783, 9260, 14108, 9489, 4830, 28002, 21885, 16954, 15868, 16902, 19634, 22134, 24960, 11625, 10090, 13084, 20795, 8160, 11223, 5578, 1741];
|
||||
nov = [0, 13537, 30169, 11276, 6862, 13438, 10021, 14033, 11938, 10941, 3634, 23317, 33661, 22821, 30040, 13944, 13980, 10466, 8872, 9020, 10166, 37281, 31896, 15089, 15184, 14277, 8070, 13321, 11396, 9223];
|
||||
dec = [8826, 11600, 5676, 8647, 8214, 26350, 12609, 9070, 21900, 17233, 11803, 17161, 12652, 2534, 5066, 26487, 27623, 21798, 17840, 11337, 3662, 9396, 16589, 242, 0, 0, 2674, 1892, 98, 10846, 9384];
|
||||
|
||||
// the maximum number of XPs in a day
|
||||
max = 111087;
|
||||
|
||||
// the minimum number of XPs in a day
|
||||
min = 0;
|
||||
|
||||
// Left aligned text for the username
|
||||
text = "Aviortheking";
|
||||
// -- TO CHANGE END -- //
|
||||
|
||||
// Right aligned text for the year
|
||||
year = "2021";
|
||||
/* TO CHANGE END -*/
|
||||
|
||||
// Define the maximum height
|
||||
maxHeight = 100;
|
||||
|
||||
// Define the spacing between values
|
||||
spacing = 0;
|
||||
|
||||
socleHeight = 20;
|
||||
textHeight = 10;
|
||||
// Define the base height
|
||||
baseHeight = 20;
|
||||
|
||||
// Define the text height
|
||||
textHeight = baseHeight / 2;
|
||||
|
||||
// define the minimum value to be before displaying
|
||||
minValue = 0;
|
||||
|
||||
barSize = 10;
|
||||
|
||||
// 33 = 31 days + 2 border)
|
||||
baseWidth = barSize * 33;
|
||||
|
||||
// 14 = 12 months + 2 border
|
||||
baseLength = barSize * 14;
|
||||
|
||||
|
||||
// Function that generate a whole month
|
||||
module generateMonth(month, offset = 0) {
|
||||
|
||||
// loop through each days for the month
|
||||
for (index = [0 : len(month) -1 ]) {
|
||||
|
||||
// get the XP
|
||||
it = month[index];
|
||||
translate([10 + (10 + spacing) * index,10 + (10 + spacing) * offset, socleHeight - 1]) {
|
||||
cube([10,10,it < minValue ? 0 : it * maxHeight / max + 1]);
|
||||
|
||||
// Render the bar
|
||||
translate([
|
||||
barSize + (barSize + spacing) * index,
|
||||
barSize + (barSize + spacing) * offset,
|
||||
baseHeight - 1 // put it in the bottom part to make sure they are one
|
||||
]) {
|
||||
cube([
|
||||
barSize,
|
||||
barSize,
|
||||
it < minValue ? 0 : it * maxHeight / max + 1 // make it higher for the reason of the comment above
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// merge everyting
|
||||
union() {
|
||||
// remove part of bottom with text
|
||||
difference() {
|
||||
cube([330, 140, socleHeight]);
|
||||
translate([5,1.9,(socleHeight - textHeight) / 2]) {
|
||||
rotate([90,0,0]) {
|
||||
|
||||
cube([baseWidth, baseLength, baseHeight]);
|
||||
|
||||
// if year is specified
|
||||
if (len(year) > 0) {
|
||||
|
||||
// move the year to the right
|
||||
translate([baseWidth - 5, 1.9, (baseHeight - textHeight) / 2]) {
|
||||
|
||||
// rotate it
|
||||
rotate([90, 0, 0]) {
|
||||
|
||||
// extrude the base
|
||||
linear_extrude(2)
|
||||
text(year, size=textHeight, halign="right");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// move the username to the left
|
||||
translate([5, 1.9, (baseHeight - textHeight) / 2]) {
|
||||
|
||||
// rotate it
|
||||
rotate([90, 0, 0]) {
|
||||
|
||||
// extrude the base
|
||||
linear_extrude(2)
|
||||
text(text, size=textHeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Generate each months :D
|
||||
months = [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec];
|
||||
|
||||
for (monthIndex = [ 0 : 11 ]) {
|
||||
if (monthIndex == 0) {
|
||||
generateMonth(jan, monthIndex);
|
||||
}
|
||||
if (monthIndex == 1) {
|
||||
generateMonth(feb, monthIndex);
|
||||
}
|
||||
if (monthIndex == 2) {
|
||||
generateMonth(mar, monthIndex);
|
||||
}
|
||||
if (monthIndex == 3) {
|
||||
generateMonth(apr, monthIndex);
|
||||
}
|
||||
if (monthIndex == 4) {
|
||||
generateMonth(may, monthIndex);
|
||||
}
|
||||
if (monthIndex == 5) {
|
||||
generateMonth(jun, monthIndex);
|
||||
}
|
||||
if (monthIndex == 6) {
|
||||
generateMonth(jul, monthIndex);
|
||||
}
|
||||
if (monthIndex == 7) {
|
||||
generateMonth(aug, monthIndex);
|
||||
}
|
||||
if (monthIndex == 8) {
|
||||
generateMonth(sep, monthIndex);
|
||||
}
|
||||
if (monthIndex == 9) {
|
||||
generateMonth(oct, monthIndex);
|
||||
}
|
||||
|
||||
if (monthIndex == 10) {
|
||||
generateMonth(nov, monthIndex);
|
||||
}
|
||||
if (monthIndex == 11) {
|
||||
generateMonth(dec, monthIndex);
|
||||
}
|
||||
generateMonth(months[monthIndex], monthIndex);
|
||||
}
|
||||
}
|
||||
|
@ -3,12 +3,12 @@
|
||||
#########
|
||||
# openscad doesn't seems to work on Alpine Linux :(
|
||||
# FROM docker.io/node:alpine as IMAGE
|
||||
FROM docker.io/node:latest as IMAGE
|
||||
FROM docker.io/node:slim as IMAGE
|
||||
|
||||
# External deps (for node-gyp add: "python3 make g++")
|
||||
# git is used to install the npm packages with git deps
|
||||
RUN apt-get update &&\
|
||||
apt-get install git openscad -y
|
||||
apt-get install openscad -y
|
||||
|
||||
# run as non root user
|
||||
USER node
|
||||
|
14
README.md
14
README.md
@ -20,8 +20,20 @@ https://codestats-skyline.avior.me/?username=Aviortheking
|
||||
|
||||
It will generate and allows you to download the generated STL!
|
||||
|
||||
### render a specific year
|
||||
|
||||
You can also render a specific year!
|
||||
|
||||
simply add the year to the URL
|
||||
|
||||
```
|
||||
https://codestats-skyline.avior.me/?username=Aviortheking&year=2021
|
||||
```
|
||||
|
||||
and voilà!
|
||||
|
||||
## Deploy the tool yourself!
|
||||
|
||||
You can use the Dockerfile to deploy the tool yourself!
|
||||
|
||||
_Code will be refactored as it has been done quickly :p_
|
||||
_Code will be refactored as it has been done quickly :p_
|
||||
|
23
index.ts
23
index.ts
@ -5,22 +5,35 @@ import { generateThingy } from './main'
|
||||
const app = express()
|
||||
|
||||
app.get('/', async (req, res) => {
|
||||
const { username } = req.query
|
||||
|
||||
// Fetch the query
|
||||
const { username, year } = req.query as Record<string, string>
|
||||
|
||||
// if no username is sent in the request return this simple HTML lul
|
||||
if (!username) {
|
||||
res.status(400).send('Error: missing username parameter in query')
|
||||
return
|
||||
}
|
||||
|
||||
const bfr = await generateThingy(username as string)
|
||||
// transform the year intoo a number
|
||||
const yearN = year ? isNaN(parseInt(year)) ? undefined : parseInt(year) : undefined
|
||||
|
||||
res.setHeader('Content-Disposition', `attachment; filename=${username}.stl`)
|
||||
res.status(200).send(bfr)
|
||||
// Generate the STL
|
||||
const bfr = await generateThingy(username, yearN)
|
||||
|
||||
// force download the STL with a good name
|
||||
res
|
||||
.setHeader('Content-Disposition', `attachment; filename=${username}-${year ? year : 'full'}.stl`)
|
||||
.status(200)
|
||||
.send(bfr)
|
||||
})
|
||||
|
||||
// Create the server
|
||||
const server = http
|
||||
.createServer(app)
|
||||
|
||||
// start the server on the specified port or the 3000
|
||||
const port = process.env.PORT ? parseInt(process.env.PORT) : 3000
|
||||
server.listen(port, '0.0.0.0')
|
||||
console.log(`Server started on port ${port}!`)
|
||||
|
||||
console.log(`Server started on port ${port}!`)
|
||||
|
71
main.ts
71
main.ts
@ -2,7 +2,7 @@ import { objectLoop, objectValues } from '@dzeio/object-util'
|
||||
import fetch from 'node-fetch'
|
||||
import { promises as fs } from 'fs'
|
||||
import { CodeStatsJSON } from './interfaces'
|
||||
// @ts-expect-error no typing for this lib
|
||||
// @ts-expect-error no typing available for this lib
|
||||
import nodescad from 'nodescad'
|
||||
|
||||
const months = [
|
||||
@ -30,14 +30,32 @@ async function exists(path: string) {
|
||||
}
|
||||
}
|
||||
|
||||
export const generateThingy = async (username: string) => {
|
||||
export const generateThingy = async (username: string, filterYear?: number): Promise<Buffer | null> => {
|
||||
|
||||
const res = await fetch(`https://codestats.net/api/users/${username}`)
|
||||
|
||||
// Start generation
|
||||
const now = new Date()
|
||||
console.log(`Generating for ${username} (year = ${filterYear})`)
|
||||
|
||||
// Fetch from code::stats the informations (currently no cache is used, but is too much requests are sent one will be put)
|
||||
const res = await fetch(`https://codestats.net/api/users/${username}`, {
|
||||
headers: {
|
||||
// Identify the software
|
||||
'User-Agent': '@aviortheking/codestats-skyline'
|
||||
}
|
||||
})
|
||||
|
||||
if (res.status !== 200) {
|
||||
console.error(`Could not generate STL, Code::Stats is being mean to me (statusCode: ${res.status})`)
|
||||
return null
|
||||
}
|
||||
|
||||
// Response from Code::Stats
|
||||
const data = await res.json() as CodeStatsJSON
|
||||
|
||||
// object that will contain aggregated values
|
||||
const correctedData: Record<string, Array<number>> = {}
|
||||
|
||||
// Fill the arrays
|
||||
for (const month of months) {
|
||||
if (month === '') {
|
||||
continue
|
||||
@ -45,50 +63,58 @@ export const generateThingy = async (username: string) => {
|
||||
correctedData[month] = Array(31).fill(0)
|
||||
}
|
||||
|
||||
// loop through each dates
|
||||
objectLoop(data.dates, (value, key) => {
|
||||
// get the year-month-day from the key
|
||||
const [yearTxt, monthTxt, dayTxt] = key.split('-')
|
||||
// if we want to add a way to filter by year we can by using the `_` var
|
||||
const [_, month, day] = [parseInt(yearTxt), parseInt(monthTxt), parseInt(dayTxt)]
|
||||
if (!(months[month] in correctedData)) {
|
||||
correctedData[months[month]] = []
|
||||
}
|
||||
if (!(day in correctedData[months[month]])) {
|
||||
correctedData[months[month]][day] = 0
|
||||
const [year, month, day] = [parseInt(yearTxt), parseInt(monthTxt), parseInt(dayTxt)]
|
||||
|
||||
// if there is year filtering and it is not the correct year, filter out
|
||||
if (filterYear && filterYear !== year) {
|
||||
return
|
||||
}
|
||||
|
||||
// add XP points to the new object
|
||||
correctedData[months[month]][day] += value
|
||||
})
|
||||
|
||||
// get the maximum value from the list
|
||||
const max = objectValues(correctedData).reduce((p, c) => {
|
||||
const monthMax = Math.max(...c)
|
||||
const monthMax = Math.max(...c.slice(1))
|
||||
return monthMax > p ? monthMax : p
|
||||
}, 0)
|
||||
|
||||
// get the minimum value from the list
|
||||
const min = objectValues(correctedData).reduce((p, c) => {
|
||||
const monthMax = Math.min(...c)
|
||||
const monthMax = Math.min(...c.slice(1))
|
||||
return monthMax < p ? monthMax : p
|
||||
}, 0)
|
||||
|
||||
// prepare the file replacement
|
||||
let final = ''
|
||||
|
||||
// Add each months
|
||||
objectLoop(correctedData, (value, key) => {
|
||||
final += `${key} = [${value.slice(1).join(', ')}];\n`
|
||||
})
|
||||
|
||||
// Add additionnal informations for generation
|
||||
final += `max = ${max};\n`
|
||||
final += `min = ${min};\n`
|
||||
final += `text = "${username}";`
|
||||
|
||||
console.log('Output:')
|
||||
console.log(final)
|
||||
final += `text = "${username}";\n`
|
||||
final += `year = ${filterYear};\n`
|
||||
|
||||
// handle prod file
|
||||
// Fetch the CAD file
|
||||
let cadPath = './3d.scad'
|
||||
if (!await exists(cadPath)) {
|
||||
cadPath = '../3d.scad'
|
||||
}
|
||||
|
||||
const file = await fs.readFile(cadPath, 'utf-8')
|
||||
const modified = file.replace(/\/\/.+\/\//gs, final)
|
||||
// await fs.writeFile('output.scad', file.replace(/\/\/.+\/\//gs, final))
|
||||
|
||||
// Modify the file content with our previous values
|
||||
const modified = file.replace(/\/\*-.+-\*\//gs, final)
|
||||
|
||||
// Render the file to an STL with OpenSCAD
|
||||
const stl = await new Promise<any>((res, rej) => {
|
||||
nodescad.render({
|
||||
input: modified
|
||||
@ -102,5 +128,8 @@ export const generateThingy = async (username: string) => {
|
||||
})
|
||||
})
|
||||
|
||||
console.log(`Finished generating for ${username} (year = ${filterYear}, timeToGenerate = ${new Date().getTime() - now.getTime()}ms)`)
|
||||
|
||||
// return the buffer
|
||||
return stl.buffer as Buffer
|
||||
}
|
||||
|
5302
package-lock.json
generated
5302
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -7,17 +7,17 @@
|
||||
"start": "node dist/index.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@dzeio/config": "^1.1.8",
|
||||
"@dzeio/object-util": "^1.4.2",
|
||||
"express": "^4.18.2",
|
||||
"node-fetch": "^2",
|
||||
"nodescad": "^1.1.0",
|
||||
"typescript": "^4.9.3"
|
||||
"nodescad": "^1.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@dzeio/config": "^1.1.8",
|
||||
"@types/express": "^4.17.14",
|
||||
"@types/node-fetch": "^2",
|
||||
"ts-node": "^10.9.1",
|
||||
"ts-node-dev": "^2.0.0"
|
||||
"ts-node-dev": "^2.0.0",
|
||||
"typescript": "^4.9.3"
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user