Skip to content

Commit

Permalink
Merge pull request #18 from austenstone/working-on-new-api
Browse files Browse the repository at this point in the history
Working on new api
  • Loading branch information
austenstone authored Nov 7, 2024
2 parents 8d85cec + 6cc0eab commit 6138477
Show file tree
Hide file tree
Showing 18 changed files with 1,313 additions and 297 deletions.
123 changes: 101 additions & 22 deletions backend/src/controllers/metrics.controller.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,112 @@
import { Request, Response } from 'express';
import { Metrics, Breakdown } from '../models/metrics.model';
import { MetricDaily, MetricDotcomChatMetrics, MetricDotcomChatModelStats, MetricEditor, MetricIdeChatEditor, MetricIdeChatMetrics, MetricIdeChatModelStats, MetricIdeCompletions, MetricLanguageStats, MetricModelStats, MetricPrMetrics, MetricPrModelStats, MetricPrRepository } from '../models/metrics.model';
import { Op } from 'sequelize';

class MetricsController {
// Get all metrics 📊
async getAllMetrics(req: Request, res: Response): Promise<void> {
async getMetrics(req: Request, res: Response): Promise<void> {
const { type, since, until, editor, language, model } = req.query;
try {
const metrics = await Metrics.findAll({
include: [Breakdown]
});
res.status(200).json(metrics); // 🎉 All metrics retrieved!
} catch (error) {
res.status(500).json(error); // 🚨 Error handling
}
}
// consider the fact that these are UTC dates...
const dateFilter: any = { };
if (since) {
dateFilter[Op.gte] = new Date(since as string);
}
if (until) {
dateFilter[Op.lte] = new Date(until as string);
}

// Get metrics by day 📅
async getMetricsByDay(req: Request, res: Response): Promise<void> {
try {
const { day } = req.params;
const metrics = await Metrics.findOne({
where: { day },
include: [Breakdown]
});
if (metrics) {
res.status(200).json(metrics); // 🎉 Metrics found!
} else {
res.status(404).json({ error: 'Metrics not found' }); // 🚨 Metrics not found
const include = [];
const types = type ? (type as string).split(/[ ,]+/) : [];
if (types.length === 0 || types.includes('copilot_ide_code_completions')) {
include.push({
attributes: { exclude: ['id', 'daily_metric_id'] },
model: MetricIdeCompletions,
as: 'copilot_ide_code_completions',
include: [{
attributes: { exclude: ['id', 'ide_completion_id'] },
model: MetricEditor,
as: 'editors',
where: editor ? { name: editor } : {},
required: false,
include: [{
attributes: { exclude: ['id', 'editor_id'] },
model: MetricModelStats,
as: 'models',
where: model ? { name: model } : {},
required: false,
include: [{
attributes: { exclude: ['id', 'model_stat_id'] },
model: MetricLanguageStats,
as: 'languages',
where: language ? { name: language } : {},
required: false,
}]
}]
}]
});
}
if (types.length === 0 || types.includes('copilot_ide_chat')) {
include.push({
attributes: { exclude: ['id', 'daily_metric_id'] },
model: MetricIdeChatMetrics,
as: 'copilot_ide_chat',
include: [{
attributes: { exclude: ['id', 'chat_metrics_id'] },
model: MetricIdeChatEditor,
as: 'editors',
where: editor ? { name: editor } : {},
required: false,
include: [{
attributes: { exclude: ['id', 'editor_id'] },
model: MetricIdeChatModelStats,
as: 'models',
where: model ? { name: model } : {},
required: false,
}]
}]
});
}
if (types.length === 0 || types.includes('copilot_dotcom_chat')) {
include.push({
attributes: { exclude: ['id', 'daily_metric_id'] },
model: MetricDotcomChatMetrics,
as: 'copilot_dotcom_chat',
include: [{
attributes: { exclude: ['id', 'chat_metrics_id'] },
model: MetricDotcomChatModelStats,
as: 'models',
where: model ? { name: model } : {},
required: false,
}]
});
}
if (types.length === 0 || types.includes('copilot_dotcom_pull_requests')) {
include.push({
attributes: { exclude: ['id', 'daily_metric_id'] },
model: MetricPrMetrics,
as: 'copilot_dotcom_pull_requests',
include: [{
attributes: { exclude: ['id', 'pr_metrics_id'] },
model: MetricPrRepository,
as: 'repositories',
include: [{
attributes: { exclude: ['id', 'repository_id'] },
model: MetricPrModelStats,
as: 'models',
where: model ? { name: model } : {},
required: false,
}]
}]
});
}
const metrics = await MetricDaily.findAll({
where: Object.getOwnPropertySymbols(dateFilter).length ? { date: dateFilter } : {},
include
});
res.status(200).json(metrics); // 🎉 All metrics retrieved!
} catch (error) {
console.log(error)
res.status(500).json(error); // 🚨 Error handling
}
}
Expand Down
22 changes: 11 additions & 11 deletions backend/src/controllers/seats.controller.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { Request, Response } from 'express';
import { Seat, Assignee, AssigningTeam } from '../models/copilot.seats';
import { CopilotSeat, CopilotAssignee, CopilotAssigningTeam } from '../models/copilot.seats';

class SeatsController {
// Get all metrics 📊
async getAllSeats(req: Request, res: Response): Promise<void> {
try {
const metrics = await Seat.findAll({
const metrics = await CopilotSeat.findAll({
include: [
{ model: Assignee, as: 'assignee' },
{ model: AssigningTeam, as: 'assigning_team' }
{ model: CopilotAssignee, as: 'assignee' },
{ model: CopilotAssigningTeam, as: 'assigning_team' }
]
});
res.status(200).json(metrics); // 🎉 All metrics retrieved!
Expand All @@ -21,18 +21,18 @@ class SeatsController {
async getSeatByLogin(req: Request, res: Response): Promise<void> {
try {
const { login } = req.params;
const assignee = await Assignee.findOne({
const assignee = await CopilotAssignee.findOne({
where: { login }
});
if (!assignee) {
res.status(404).json({ error: 'Assignee not found' }); // 🚨 Assignee not foun
return;
}
const metrics = await Seat.findOne({
const metrics = await CopilotSeat.findOne({
where: { assigneeId: assignee?.dataValues.id },
include: [
{ model: Assignee, as: 'assignee' },
{ model: AssigningTeam, as: 'assigning_team' }
{ model: CopilotAssignee, as: 'assignee' },
{ model: CopilotAssigningTeam, as: 'assigning_team' }
]
});
if (metrics) {
Expand All @@ -49,11 +49,11 @@ class SeatsController {
async getSeatActivityByLogin(req: Request, res: Response): Promise<void> {
try {
const { login } = req.params;
const metrics = await Seat.findAndCountAll({
const metrics = await CopilotSeat.findAndCountAll({
where: { login },
include: [
{ model: Assignee, as: 'assignee' },
{ model: AssigningTeam, as: 'assigning_team' }
{ model: CopilotAssignee, as: 'assignee' },
{ model: CopilotAssigningTeam, as: 'assigning_team' }
]
});
if (metrics) {
Expand Down
18 changes: 18 additions & 0 deletions backend/src/controllers/usage.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Request, Response } from 'express';
import { Usage, UsageBreakdown } from '../models/usage.model';

class UsageController {
// Get all metrics 📊
async getUsage(req: Request, res: Response): Promise<void> {
try {
const metrics = await Usage.findAll({
include: [UsageBreakdown]
});
res.status(200).json(metrics); // 🎉 All metrics retrieved!
} catch (error) {
res.status(500).json(error); // 🚨 Error handling
}
}
}

export default new UsageController();
29 changes: 22 additions & 7 deletions backend/src/database.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,47 @@
import { Sequelize } from 'sequelize';
import { Settings } from './models/settings.model';
import { Metrics, Breakdown } from './models/metrics.model';
import { Survey } from './models/survey.model';
import { Assignee, AssigningTeam, Seat } from './models/copilot.seats';
import logger from './services/logger';
import mysql2 from 'mysql2/promise';

const sequelize = new Sequelize({
dialect: 'mysql',
database: process.env.MYSQL_DATABASE || 'value',
username: process.env.MYSQL_USER || 'root',
password: process.env.MYSQL_PASSWORD || 'octocat',
host: process.env.MYSQL_HOST || 'localhost',
port: parseInt(process.env.MYSQL_PORT || '3306'),
username: process.env.MYSQL_USER || 'root',
password: process.env.MYSQL_PASSWORD || 'octocat',
database: process.env.MYSQL_DATABASE || 'value',
logging: (sql: string, timing?: number) => {
logger.info(sql);
}
});

const dbConnect = async () => {
try {
const connection = await mysql2.createConnection({
host: process.env.MYSQL_HOST || 'localhost',
port: parseInt(process.env.MYSQL_PORT || '3306'),
user: process.env.MYSQL_USER || 'root',
password: process.env.MYSQL_PASSWORD || 'octocat',
});

const query = await connection.query(`CREATE DATABASE IF NOT EXISTS \`value\`;`,);

console.log(query);
await connection.end();
} catch (error) {
logger.error('Unable to connect to the database', error);
throw error;
}
try {
await sequelize.authenticate()
await sequelize.sync({ force: false }).then(() => {
logger.info('All models were synchronized successfully. 🚀');
}).catch((error) => {
console.log(error);
logger.error('Error synchronizing models', error);
});
} catch (error) {
logger.info('Unable to initialize the database', error);
throw error;
}
};

Expand Down
75 changes: 64 additions & 11 deletions backend/src/models/copilot.seats.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { DataTypes } from 'sequelize';
import { sequelize } from '../database';

const Assignee = sequelize.define('Assignee', {
const CopilotAssignee = sequelize.define('CopilotAssignee', {
login: {
type: DataTypes.STRING,
allowNull: false,
Expand Down Expand Up @@ -75,11 +75,10 @@ const Assignee = sequelize.define('Assignee', {
allowNull: false,
},
}, {
tableName: 'assignees',
timestamps: false,
});

const AssigningTeam = sequelize.define('AssigningTeam', {
const CopilotAssigningTeam = sequelize.define('CopilotAssigningTeam', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
Expand Down Expand Up @@ -133,11 +132,10 @@ const AssigningTeam = sequelize.define('AssigningTeam', {
allowNull: true,
},
}, {
tableName: 'assigning_teams',
timestamps: false,
});

const Seat = sequelize.define('Seat', {
const CopilotSeat = sequelize.define('CopilotSeat', {
created_at: {
type: DataTypes.DATE,
allowNull: false,
Expand Down Expand Up @@ -165,23 +163,78 @@ const Seat = sequelize.define('Seat', {
assigneeId: {
type: DataTypes.INTEGER,
references: {
model: Assignee,
model: CopilotAssignee,
key: 'id',
}
},
assigningTeamId: {
type: DataTypes.INTEGER,
references: {
model: AssigningTeam,
model: CopilotAssigningTeam,
key: 'id',
},
},
}, {
tableName: 'seats',
timestamps: false,
});

Seat.belongsTo(Assignee, { foreignKey: 'assigneeId', as: 'assignee' });
Seat.belongsTo(AssigningTeam, { foreignKey: 'assigningTeamId', as: 'assigning_team' });
CopilotSeat.belongsTo(CopilotAssignee, { foreignKey: 'assigneeId', as: 'assignee' });
CopilotSeat.belongsTo(CopilotAssigningTeam, { foreignKey: 'assigningTeamId', as: 'assigning_team' });

export { Assignee, AssigningTeam, Seat };
async function insertSeats(data: any[]) {
for (const seat of data) {
const assignee = await CopilotAssignee.findOrCreate({
where: { id: seat.assignee.id },
defaults: {
login: seat.assignee.login,
node_id: seat.assignee.node_id,
avatar_url: seat.assignee.avatar_url,
gravatar_id: seat.assignee.gravatar_id,
url: seat.assignee.url,
html_url: seat.assignee.html_url,
followers_url: seat.assignee.followers_url,
following_url: seat.assignee.following_url,
gists_url: seat.assignee.gists_url,
starred_url: seat.assignee.starred_url,
subscriptions_url: seat.assignee.subscriptions_url,
organizations_url: seat.assignee.organizations_url,
repos_url: seat.assignee.repos_url,
events_url: seat.assignee.events_url,
received_events_url: seat.assignee.received_events_url,
type: seat.assignee.type,
site_admin: seat.assignee.site_admin,
}
});

const assigningTeam = seat.assigning_team ? await CopilotAssigningTeam.findOrCreate({
where: { id: seat.assigning_team.id },
defaults: {
node_id: seat.assigning_team.node_id,
url: seat.assigning_team.url,
html_url: seat.assigning_team.html_url,
name: seat.assigning_team.name,
slug: seat.assigning_team.slug,
description: seat.assigning_team.description,
privacy: seat.assigning_team.privacy,
notification_setting: seat.assigning_team.notification_setting,
permission: seat.assigning_team.permission,
members_url: seat.assigning_team.members_url,
repositories_url: seat.assigning_team.repositories_url,
parent: seat.assigning_team.parent,
}
}) : null;

await CopilotSeat.create({
created_at: seat.created_at,
updated_at: seat.updated_at,
pending_cancellation_date: seat.pending_cancellation_date,
last_activity_at: seat.last_activity_at,
last_activity_editor: seat.last_activity_editor,
plan_type: (seat as any).plan_type,
assigneeId: seat.assignee.id,
assigningTeamId: assigningTeam ? seat.assigning_team?.id : null,
});
}
}

export { CopilotAssignee, CopilotAssigningTeam, CopilotSeat, insertSeats };
Loading

0 comments on commit 6138477

Please sign in to comment.