Skip to content

Commit 2bdfde3

Browse files
Joshua Zhoutektaxi
authored andcommitted
update devpost url from csv file script
1 parent de51c39 commit 2bdfde3

File tree

2 files changed

+191
-0
lines changed

2 files changed

+191
-0
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,6 @@ assets/.DS_Store
6969
#secrets
7070
secret.yaml
7171
package-lock.json
72+
73+
# script data
74+
scripts/data/

scripts/update_devpost_urls.js

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
"use strict";
2+
3+
const fs = require("fs"); // filesystem to read csv
4+
const path = require("path");
5+
const readline = require("readline");
6+
const env = require("../services/env.service");
7+
const db = require("../services/database.service");
8+
const Team = require("../models/team.model");
9+
const logger = require("../services/logger.service");
10+
11+
env.load(path.join(__dirname, "../.env"));
12+
13+
// parse the csv file
14+
function parseDelimited(text, delimiter) {
15+
const rows = [];
16+
let row = [];
17+
let field = "";
18+
let inQuotes = false;
19+
20+
for (let i = 0; i < text.length; i += 1) {
21+
const char = text[i];
22+
const nextChar = text[i + 1];
23+
24+
if (inQuotes) {
25+
if (char === '"' && nextChar === '"') {
26+
field += '"';
27+
i += 1;
28+
} else if (char === '"') {
29+
inQuotes = false;
30+
} else {
31+
field += char;
32+
}
33+
continue;
34+
}
35+
36+
if (char === '"') {
37+
inQuotes = true;
38+
continue;
39+
}
40+
41+
if (char === delimiter) {
42+
row.push(field);
43+
field = "";
44+
continue;
45+
}
46+
47+
if (char === "\n") {
48+
row.push(field.replace(/\r$/, ""));
49+
rows.push(row);
50+
row = [];
51+
field = "";
52+
continue;
53+
}
54+
55+
field += char;
56+
}
57+
58+
if (field.length > 0 || row.length > 0) {
59+
row.push(field.replace(/\r$/, ""));
60+
rows.push(row);
61+
}
62+
63+
return rows;
64+
}
65+
66+
// get the column index of a given header name
67+
function findHeaderIndex(headers, name) {
68+
const target = name.trim().toLowerCase();
69+
return headers.findIndex((header) => header.trim().toLowerCase() === target);
70+
}
71+
72+
// get the filepath of the csv file used to update
73+
async function promptFilePath(defaultPath) {
74+
const rl = readline.createInterface({
75+
input: process.stdin,
76+
output: process.stdout,
77+
});
78+
79+
const answer = await new Promise((resolve) => {
80+
rl.question(`CSV path (default: ${defaultPath}): `, resolve);
81+
});
82+
83+
rl.close();
84+
return answer.trim() || defaultPath;
85+
}
86+
87+
// update devpost links for each team
88+
async function run() {
89+
// get the relative path of the csv file
90+
const defaultPath = "data/judging.csv";
91+
const inputPath = await promptFilePath(defaultPath);
92+
const resolvedPath = path.isAbsolute(inputPath)
93+
? inputPath
94+
: path.join(__dirname, inputPath);
95+
96+
// check the csv file exists
97+
if (!fs.existsSync(resolvedPath)) {
98+
logger.error(`File not found: ${resolvedPath}`);
99+
process.exit(1);
100+
}
101+
102+
// parse content and format into rows
103+
const ext = path.extname(resolvedPath).toLowerCase();
104+
const delimiter = ext === ".tsv" ? "\t" : ",";
105+
const content = fs.readFileSync(resolvedPath, "utf8");
106+
const rows = parseDelimited(content, delimiter);
107+
// console.log(rows)
108+
// [
109+
// [ 'Devpost Link', 'teamId' ],
110+
// [ 'http://devpost.com/testing', '1234567890' ],
111+
// ]
112+
113+
if (rows.length === 0) {
114+
logger.error("No rows found in file");
115+
process.exit(1);
116+
}
117+
118+
// check if required columns exist in the csv file (Devpost Link and teamId)
119+
const headers = rows[0];
120+
const devpostIndex = findHeaderIndex(headers, "Devpost Link");
121+
const teamIdIndex = findHeaderIndex(headers, "teamId");
122+
if (devpostIndex === -1 || teamIdIndex === -1) {
123+
logger.error("Missing required columns: Devpost Link, teamId");
124+
logger.error(`Your csv file has headers: ${headers.join(", ")}`);
125+
process.exit(1);
126+
}
127+
128+
// connect to db
129+
await new Promise((resolve) => db.connect(resolve));
130+
131+
// counts
132+
let updatedCount = 0;
133+
let skippedCount = 0;
134+
const missingTeams = [];
135+
136+
// iterate through rows starting at 1 (skip header row)
137+
for (let i = 1; i < rows.length; i += 1) {
138+
const row = rows[i];
139+
const teamId = (row[teamIdIndex] || "").trim();
140+
const devpostURL = (row[devpostIndex] || "").trim();
141+
142+
// missing information
143+
if (!teamId) {
144+
logger.warn(`row ${i + 1}: missing teamId, skipping`);
145+
skippedCount += 1;
146+
continue;
147+
}
148+
if (!devpostURL) {
149+
logger.warn(
150+
`row ${i + 1}: missing Devpost Link for team ${teamId}, skipping`,
151+
);
152+
skippedCount += 1;
153+
continue;
154+
}
155+
156+
// update devpost link using the team service
157+
const updatedTeam = await Team.findByIdAndUpdate(
158+
teamId,
159+
{ devpostURL },
160+
{ new: true }
161+
)
162+
.lean()
163+
.exec();
164+
165+
// handle service return null which means team is not found
166+
if (!updatedTeam) {
167+
logger.warn(`row ${i + 1}: team not found for id ${teamId}`);
168+
missingTeams.push(teamId);
169+
continue;
170+
}
171+
172+
updatedCount += 1;
173+
}
174+
175+
// print update stats
176+
logger.info(`Updated teams: ${updatedCount}`);
177+
logger.info(`Skipped rows: ${skippedCount}`);
178+
if (missingTeams.length > 0) {
179+
logger.warn(`Missing teams: ${missingTeams.join(", ")}`);
180+
}
181+
182+
process.exit(0);
183+
}
184+
185+
run().catch((err) => {
186+
logger.error(`Failed to update devpost URLs: ${err}`);
187+
process.exit(1);
188+
});

0 commit comments

Comments
 (0)