Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions gurubu-backend/controllers/jiraPdfCreatorController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
const pdfService = require("../services/pdfService");
const jiraService = require("../services/jiraService");
const fs = require("fs");
exports.downloadBoardIssuesAsPdf = async (req, res) => {
try {
const { boardId } = req.params;

if (!boardId) {
return res.status(400).json({ error: "Board ID gerekli" });
}

const result = await jiraService.getAllBoardIssuesWithInitialStoryPoints(
boardId
);

if (!result || !result.issues || result.issues.length === 0) {
return res.status(404).json({ error: "Bu board için görev bulunamadı" });
}

const pdfPath = await pdfService.generateIssuesReport(
result.issues,
boardId
);

if (!fs.existsSync(pdfPath)) {
return res
.status(500)
.json({ error: "PDF oluşturulurken bir hata oluştu" });
}

const today = new Date();
const formattedDate = today.toISOString().split("T")[0];
const formattedTime = today.toTimeString().split(" ")[0].replace(/:/g, "");
const filename = `jira_board_${boardId}_raporu_${formattedDate}_${formattedTime}.pdf`;
res.setHeader("Content-Type", "application/pdf");
res.setHeader("Content-Disposition", `attachment; filename="${filename}"`);

const fileStream = fs.createReadStream(pdfPath);
fileStream.pipe(res);

fileStream.on("close", () => {
pdfService.cleanupPdf(pdfPath);
});
} catch (error) {
console.error("PDF oluşturma hatası:", error);
res.status(500).json({
error: "PDF oluşturulurken bir hata oluştu",
details: error.message,
});
}
};
1 change: 1 addition & 0 deletions gurubu-backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"dotenv": "^16.3.1",
"express": "^4.18.2",
"openai": "^4.86.2",
"pdfkit": "^0.16.0",
"redis": "^4.6.10",
"socket.io": "^4.7.2",
"uuid": "^9.0.1"
Expand Down
7 changes: 7 additions & 0 deletions gurubu-backend/routes/jiraPdfCreatorRoutes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const express = require("express");
const router = express.Router();
const jiraPdfCreatorController = require("../controllers/jiraPdfCreatorController");

router.get("/:boardId/download-pdf", jiraPdfCreatorController.downloadBoardIssuesAsPdf);

module.exports = router;
2 changes: 2 additions & 0 deletions gurubu-backend/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@ const healthCheckRoute = require("./routes/healthCheckRoute");
const jiraRoutes = require("./routes/jiraRoutes");
const pRoutes = require("./routes/pRoutes");
const storyPointRoutes = require("./routes/storyPointRoutes");
const jiraPdfCreatorRoutes = require("./routes/jiraPdfCreatorRoutes");

app.use("/room", cors(corsOptions), roomRoutes);
app.use("/healthcheck", cors(corsOptions), healthCheckRoute);
app.use("/jira", cors(corsOptions), jiraRoutes);
app.use("/jira-pdf-creator", cors(corsOptions), jiraPdfCreatorRoutes);
app.use("/p", cors(corsOptions), pRoutes);
app.use("/storypoint", cors(corsOptions), storyPointRoutes);

Expand Down
120 changes: 120 additions & 0 deletions gurubu-backend/services/jiraService.js
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,126 @@ class JiraService {
throw error;
}
}

async getAllBoardIssuesWithInitialStoryPoints(boardId) {
try {
let allIssues = [];
let startAt = 0;
const maxResults = 100;
let total = null;

do {
console.log(`Fetching issues ${startAt} to ${startAt + maxResults}...`);
const response = await axios.get(
`${this.baseUrl}/rest/agile/1.0/board/${boardId}/issue`,
{
params: {
expand: 'changelog',
startAt: startAt,
maxResults: maxResults,
fields: 'key,assignee,changelog,customfield_10002,summary,description,' +
(process.env.JIRA_PROJECT_KEY_ONE ? process.env.JIRA_PROJECT_KEY_ONE + ',' : '') +
(process.env.JIRA_PROJECT_KEY_TWO ? process.env.JIRA_PROJECT_KEY_TWO + ',' : '') +
(process.env.JIRA_PROJECT_KEY_THREE ? process.env.JIRA_PROJECT_KEY_THREE + ',' : '') +
(process.env.JIRA_PROJECT_KEY_FOUR ? process.env.JIRA_PROJECT_KEY_FOUR : '')
},
auth: this.auth,
headers: {
Accept: "application/json",
},
}
);

if (total === null) {
total = response.data.total;
}

const processedIssues = response.data.issues.map(issue => {
let initialStoryPoint = null;
const currentStoryPoint = issue.fields[process.env.JIRA_PROJECT_KEY_FOUR] ||
issue.fields.customfield_10002;

let sortedHistories = [];
let firstStoryPointHistory = null;

if (issue.changelog && issue.changelog.histories) {
sortedHistories = issue.changelog.histories.sort(
(a, b) => new Date(a.created) - new Date(b.created)
);

for (const history of sortedHistories) {
for (const item of history.items) {
if (
item.field === process.env.JIRA_PROJECT_KEY_FOUR ||
item.field === "Story Points"
) {

if (item.fromString && !isNaN(parseFloat(item.fromString))) {
initialStoryPoint = parseFloat(item.fromString);
firstStoryPointHistory = history;
break;
}
else if (!firstStoryPointHistory && item.toString && !isNaN(parseFloat(item.toString))) {
initialStoryPoint = parseFloat(item.toString);
firstStoryPointHistory = history;
break;
}
}
}
if (firstStoryPointHistory) break;
}
}

if (initialStoryPoint === null && currentStoryPoint) {
initialStoryPoint = currentStoryPoint;
}

return {
key: issue.key,
summary: issue.fields.summary || null,
description: issue.fields.description || null,
initialStoryPoint,
currentStoryPoint
};
})
.filter(issue => {
if (issue.summary === "Sprint Leftover") {
return false;
}

if (!issue.initialStoryPoint || !issue.currentStoryPoint ||
isNaN(issue.initialStoryPoint) || isNaN(issue.currentStoryPoint)) {
return false;
}

return true;
});

allIssues = [...allIssues, ...processedIssues];

startAt += maxResults;

console.log(`Processed ${allIssues.length} of ${total} issues...`);

} while (startAt < total);

return {
maxResults: maxResults,
startAt: 0,
total: total,
issues: allIssues
};
} catch (error) {
if (axios.isAxiosError(error)) {
console.error("Axios error details:", {
status: error.response?.status,
statusText: error.response?.statusText,
data: error.response?.data,
});
}
throw error;
}
}
}

module.exports = new JiraService();
Loading