Skip to content

Commit

Permalink
Merge pull request #7200 from ever-co/feat/screenshot-analyze-ai
Browse files Browse the repository at this point in the history
[Feat] Screenshot Analyze Using Gauzy AI
  • Loading branch information
rahul-rocket authored Nov 29, 2023
2 parents 47d8d43 + 890270c commit 1e084ab
Show file tree
Hide file tree
Showing 15 changed files with 536 additions and 120 deletions.
6 changes: 5 additions & 1 deletion .cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,11 @@
"gridcell",
"scrollgrid",
"r_liteprofile",
"r_emailaddress"
"r_emailaddress",
"Signoz",
"OTEL",
"OTLP",
"opentelemetry"
],
"useGitignore": true,
"ignorePaths": [
Expand Down
16 changes: 8 additions & 8 deletions packages/contracts/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
export * from './accounting-template.model';
/** App Setting Model */
export * from './app.model';
export * from './appointment-employees.model';
export * from './approval-policy.model';
export * from './availability-slots.model';
Expand Down Expand Up @@ -47,6 +49,7 @@ export * from './expense.model';
export * from './feature.model';
export * from './file-provider';
export * from './geo-location.model';
export * from './github.model';
export * from './goal-settings.model';
export * from './goals.model';
export * from './help-center-article.model';
Expand Down Expand Up @@ -77,6 +80,7 @@ export * from './organization-positions.model';
export * from './organization-projects.model';
export * from './organization-recurring-expense.model';
export * from './organization-sprint.model';
export * from './organization-task-setting.model';
export * from './organization-team-employee-model';
export * from './organization-team-join-request.model';
export * from './organization-team.model';
Expand All @@ -96,17 +100,19 @@ export * from './request-approval-team.model';
export * from './request-approval.model';
export * from './role-permission.model';
export * from './role.model';
export * from './screenshot.model';
export * from './seed.model';
export * from './skill-entity.model';
export * from './sms.model';
export * from './tag.model';
export * from './task-estimation.model';
export * from './task-linked-issue.model';
export * from './task-priority.model';
export * from './task-size.model';
export * from './task-related-issue-type.model';
export * from './task-size.model';
export * from './task-status.model';
export * from './task-version.model';
export * from './task.model';
export * from './task-linked-issue.model';
export * from './tenant.model';
export * from './time-off.model';
export * from './timesheet-statistics.model';
Expand All @@ -117,12 +123,6 @@ export * from './upwork.model';
export * from './user-organization.model';
export * from './user.model';
export * from './wakatime.model';
export * from './organization-task-setting.model';
export * from './task-estimation.model';
export * from './github.model';

/** App Setting Model */
export * from './app.model';

export { IBaseEntityModel as BaseEntityModel } from './base-entity.model';
export {
Expand Down
40 changes: 40 additions & 0 deletions packages/contracts/src/screenshot.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { IBasePerTenantAndOrganizationEntityModel } from "./base-entity.model";
import { FileStorageProviderEnum } from "./file-provider";
import { ITimeSlot } from "./timesheet.model";
import { IRelationalUser } from "./user.model";

export interface IScreenshot extends IBasePerTenantAndOrganizationEntityModel, IRelationalUser {
[x: string]: any;
file: string;
thumb?: string;
fileUrl?: string;
thumbUrl?: string;
fullUrl?: string;
recordedAt?: Date;
storageProvider?: FileStorageProviderEnum;
/** Image/Screenshot Analysis Through Gauzy AI */
isWorkRelated?: boolean;
description?: string;
apps?: string | string[];
/** Relations */
timeSlot?: ITimeSlot;
timeSlotId?: ITimeSlot['id'];
}

export interface IScreenshotMap {
startTime: string;
endTime: string;
timeSlots: ITimeSlot[];
}

export interface IUpdateScreenshotInput extends ICreateScreenshotInput {
id: string;
}

export interface ICreateScreenshotInput extends IBasePerTenantAndOrganizationEntityModel {
activityTimestamp: string;
employeeId?: string;
file: string;
thumb?: string;
recordedAt: Date | string;
}
36 changes: 2 additions & 34 deletions packages/contracts/src/timesheet.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ import { ITask } from './task.model';
import { ITag } from './tag.model';
import { IPaginationInput } from './core.model';
import { ReportGroupByFilter } from './report.model';
import { FileStorageProviderEnum } from './file-provider';
import { IRelationalUser, IUser } from './user.model';
import { IUser } from './user.model';
import { IRelationalOrganizationTeam } from './organization-team.model';
import { IScreenshot } from './screenshot.model';

export interface ITimesheet extends IBasePerTenantAndOrganizationEntityModel {
employee: IEmployee;
Expand Down Expand Up @@ -309,39 +309,7 @@ export interface IURLMetaData {
[x: string]: any;
}

export interface IUpdateScreenshotInput extends ICreateScreenshotInput {
id: string;
}

export interface ICreateScreenshotInput
extends IBasePerTenantAndOrganizationEntityModel {
activityTimestamp: string;
employeeId?: string;
file: string;
thumb?: string;
recordedAt: Date | string;
}

export interface IScreenshot
extends IBasePerTenantAndOrganizationEntityModel,
IRelationalEmployee,
IRelationalUser {
[x: string]: any;
timeSlot?: ITimeSlot;
timeSlotId?: ITimeSlot['id'];
file: string;
thumb?: string;
fileUrl?: string;
thumbUrl?: string;
recordedAt?: Date;
storageProvider?: FileStorageProviderEnum;
}

export interface IScreenshotMap {
startTime: string;
endTime: string;
timeSlots: ITimeSlot[];
}

export interface ITimerStatusInput
extends ITimeLogTodayFilters,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@

import { MigrationInterface, QueryRunner } from "typeorm";

export class AlterScreenshotTable1701081154869 implements MigrationInterface {

name = 'AlterScreenshotTable1701081154869';

/**
* Up Migration
*
* @param queryRunner
*/
public async up(queryRunner: QueryRunner): Promise<any> {
if (['sqlite', 'better-sqlite3'].includes(queryRunner.connection.options.type)) {
await this.sqliteUpQueryRunner(queryRunner);
} else {
await this.postgresUpQueryRunner(queryRunner);
}
}

/**
* Down Migration
*
* @param queryRunner
*/
public async down(queryRunner: QueryRunner): Promise<any> {
if (['sqlite', 'better-sqlite3'].includes(queryRunner.connection.options.type)) {
await this.sqliteDownQueryRunner(queryRunner);
} else {
await this.postgresDownQueryRunner(queryRunner);
}
}

/**
* PostgresDB Up Migration
*
* @param queryRunner
*/
public async postgresUpQueryRunner(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query(`ALTER TABLE "screenshot" ADD "isWorkRelated" boolean`);
await queryRunner.query(`ALTER TABLE "screenshot" ADD "description" character varying`);
await queryRunner.query(`ALTER TABLE "screenshot" ADD "apps" json`);
await queryRunner.query(`CREATE INDEX "IDX_1b0867d86ead2332f3d4edba7d" ON "screenshot" ("isWorkRelated") `);
await queryRunner.query(`CREATE INDEX "IDX_eea7986acfb827bf5d0622c41f" ON "screenshot" ("description") `);
}

/**
* PostgresDB Down Migration
*
* @param queryRunner
*/
public async postgresDownQueryRunner(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query(`DROP INDEX "public"."IDX_eea7986acfb827bf5d0622c41f"`);
await queryRunner.query(`DROP INDEX "public"."IDX_1b0867d86ead2332f3d4edba7d"`);
await queryRunner.query(`ALTER TABLE "screenshot" DROP COLUMN "apps"`);
await queryRunner.query(`ALTER TABLE "screenshot" DROP COLUMN "description"`);
await queryRunner.query(`ALTER TABLE "screenshot" DROP COLUMN "isWorkRelated"`);
}

/**
* SqliteDB and BetterSQlite3DB Up Migration
*
* @param queryRunner
*/
public async sqliteUpQueryRunner(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query(`DROP INDEX "IDX_892e285e1da2b3e61e51e50628"`);
await queryRunner.query(`DROP INDEX "IDX_742688858e0484d66f04e4d4c4"`);
await queryRunner.query(`DROP INDEX "IDX_2b374e5cdee1145ebb2a832f20"`);
await queryRunner.query(`DROP INDEX "IDX_3d7feb5fe793e4811cdb79f983"`);
await queryRunner.query(`DROP INDEX "IDX_235004f3dafac90692cd64d915"`);
await queryRunner.query(`DROP INDEX "IDX_0951aacffe3f8d0cff54cf2f34"`);
await queryRunner.query(`DROP INDEX "IDX_5b594d02d98d5defcde323abe5"`);
await queryRunner.query(`DROP INDEX "IDX_fa1896dc735403799311968f7e"`);
await queryRunner.query(`CREATE TABLE "temporary_screenshot" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "tenantId" varchar, "organizationId" varchar, "file" varchar NOT NULL, "thumb" varchar, "recordedAt" datetime, "deletedAt" datetime, "timeSlotId" varchar, "storageProvider" varchar, "userId" varchar, "isActive" boolean DEFAULT (1), "isArchived" boolean DEFAULT (0), "isWorkRelated" boolean, "description" varchar, "apps" text, CONSTRAINT "FK_235004f3dafac90692cd64d9158" FOREIGN KEY ("tenantId") REFERENCES "tenant" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_0951aacffe3f8d0cff54cf2f341" FOREIGN KEY ("organizationId") REFERENCES "organization" ("id") ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT "FK_5b594d02d98d5defcde323abe5b" FOREIGN KEY ("timeSlotId") REFERENCES "time_slot" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_fa1896dc735403799311968f7ec" FOREIGN KEY ("userId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)`);
await queryRunner.query(`INSERT INTO "temporary_screenshot"("id", "createdAt", "updatedAt", "tenantId", "organizationId", "file", "thumb", "recordedAt", "deletedAt", "timeSlotId", "storageProvider", "userId", "isActive", "isArchived") SELECT "id", "createdAt", "updatedAt", "tenantId", "organizationId", "file", "thumb", "recordedAt", "deletedAt", "timeSlotId", "storageProvider", "userId", "isActive", "isArchived" FROM "screenshot"`);
await queryRunner.query(`DROP TABLE "screenshot"`);
await queryRunner.query(`ALTER TABLE "temporary_screenshot" RENAME TO "screenshot"`);
await queryRunner.query(`CREATE INDEX "IDX_892e285e1da2b3e61e51e50628" ON "screenshot" ("isArchived") `);
await queryRunner.query(`CREATE INDEX "IDX_742688858e0484d66f04e4d4c4" ON "screenshot" ("isActive") `);
await queryRunner.query(`CREATE INDEX "IDX_2b374e5cdee1145ebb2a832f20" ON "screenshot" ("storageProvider") `);
await queryRunner.query(`CREATE INDEX "IDX_3d7feb5fe793e4811cdb79f983" ON "screenshot" ("recordedAt") `);
await queryRunner.query(`CREATE INDEX "IDX_235004f3dafac90692cd64d915" ON "screenshot" ("tenantId") `);
await queryRunner.query(`CREATE INDEX "IDX_0951aacffe3f8d0cff54cf2f34" ON "screenshot" ("organizationId") `);
await queryRunner.query(`CREATE INDEX "IDX_5b594d02d98d5defcde323abe5" ON "screenshot" ("timeSlotId") `);
await queryRunner.query(`CREATE INDEX "IDX_fa1896dc735403799311968f7e" ON "screenshot" ("userId") `);
await queryRunner.query(`CREATE INDEX "IDX_1b0867d86ead2332f3d4edba7d" ON "screenshot" ("isWorkRelated") `);
await queryRunner.query(`CREATE INDEX "IDX_eea7986acfb827bf5d0622c41f" ON "screenshot" ("description") `);
}

/**
* SqliteDB and BetterSQlite3DB Down Migration
*
* @param queryRunner
*/
public async sqliteDownQueryRunner(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query(`DROP INDEX "IDX_eea7986acfb827bf5d0622c41f"`);
await queryRunner.query(`DROP INDEX "IDX_1b0867d86ead2332f3d4edba7d"`);
await queryRunner.query(`DROP INDEX "IDX_fa1896dc735403799311968f7e"`);
await queryRunner.query(`DROP INDEX "IDX_5b594d02d98d5defcde323abe5"`);
await queryRunner.query(`DROP INDEX "IDX_0951aacffe3f8d0cff54cf2f34"`);
await queryRunner.query(`DROP INDEX "IDX_235004f3dafac90692cd64d915"`);
await queryRunner.query(`DROP INDEX "IDX_3d7feb5fe793e4811cdb79f983"`);
await queryRunner.query(`DROP INDEX "IDX_2b374e5cdee1145ebb2a832f20"`);
await queryRunner.query(`DROP INDEX "IDX_742688858e0484d66f04e4d4c4"`);
await queryRunner.query(`DROP INDEX "IDX_892e285e1da2b3e61e51e50628"`);
await queryRunner.query(`ALTER TABLE "screenshot" RENAME TO "temporary_screenshot"`);
await queryRunner.query(`CREATE TABLE "screenshot" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "tenantId" varchar, "organizationId" varchar, "file" varchar NOT NULL, "thumb" varchar, "recordedAt" datetime, "deletedAt" datetime, "timeSlotId" varchar, "storageProvider" varchar, "userId" varchar, "isActive" boolean DEFAULT (1), "isArchived" boolean DEFAULT (0), CONSTRAINT "FK_235004f3dafac90692cd64d9158" FOREIGN KEY ("tenantId") REFERENCES "tenant" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_0951aacffe3f8d0cff54cf2f341" FOREIGN KEY ("organizationId") REFERENCES "organization" ("id") ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT "FK_5b594d02d98d5defcde323abe5b" FOREIGN KEY ("timeSlotId") REFERENCES "time_slot" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_fa1896dc735403799311968f7ec" FOREIGN KEY ("userId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)`);
await queryRunner.query(`INSERT INTO "screenshot"("id", "createdAt", "updatedAt", "tenantId", "organizationId", "file", "thumb", "recordedAt", "deletedAt", "timeSlotId", "storageProvider", "userId", "isActive", "isArchived") SELECT "id", "createdAt", "updatedAt", "tenantId", "organizationId", "file", "thumb", "recordedAt", "deletedAt", "timeSlotId", "storageProvider", "userId", "isActive", "isArchived" FROM "temporary_screenshot"`);
await queryRunner.query(`DROP TABLE "temporary_screenshot"`);
await queryRunner.query(`CREATE INDEX "IDX_fa1896dc735403799311968f7e" ON "screenshot" ("userId") `);
await queryRunner.query(`CREATE INDEX "IDX_5b594d02d98d5defcde323abe5" ON "screenshot" ("timeSlotId") `);
await queryRunner.query(`CREATE INDEX "IDX_0951aacffe3f8d0cff54cf2f34" ON "screenshot" ("organizationId") `);
await queryRunner.query(`CREATE INDEX "IDX_235004f3dafac90692cd64d915" ON "screenshot" ("tenantId") `);
await queryRunner.query(`CREATE INDEX "IDX_3d7feb5fe793e4811cdb79f983" ON "screenshot" ("recordedAt") `);
await queryRunner.query(`CREATE INDEX "IDX_2b374e5cdee1145ebb2a832f20" ON "screenshot" ("storageProvider") `);
await queryRunner.query(`CREATE INDEX "IDX_742688858e0484d66f04e4d4c4" ON "screenshot" ("isActive") `);
await queryRunner.query(`CREATE INDEX "IDX_892e285e1da2b3e61e51e50628" ON "screenshot" ("isArchived") `);
}
}
45 changes: 27 additions & 18 deletions packages/core/src/integration-tenant/integration-tenant.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,12 @@ import { BadRequestException, Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { FindManyOptions, IsNull, Not, Repository } from 'typeorm';
import {
IBasePerTenantAndOrganizationEntityModel,
IIntegrationEntitySetting,
IIntegrationSetting,
IIntegrationTenant,
IIntegrationTenantCreateInput,
IIntegrationTenantFindInput,
IPagination,
IntegrationEnum
IPagination
} from '@gauzy/contracts';
import { RequestContext } from 'core/context';
import { TenantAwareCrudService } from 'core/crud';
Expand Down Expand Up @@ -98,38 +96,49 @@ export class IntegrationTenantService extends TenantAwareCrudService<Integration
tenantId,
organizationId,
name,
isActive: true,
isArchived: false,
integration: {
name
}
},
order: {
updatedAt: 'DESC'
provider: name,
isActive: true,
isArchived: false,
},
},
relations: {
integration: true
}
order: { updatedAt: 'DESC' },
relations: { integration: true },
});

return integration || false;
} catch (error) {
return false;
}
}

/**
*
* @param options
* @returns
* Get integration tenant settings by specified options.
* @param input - The input options for finding the integration tenant settings.
* @returns The integration tenant settings if found.
* @throws BadRequestException if not found or an error occurs.
*/
async getIntegrationSettings(
options: IBasePerTenantAndOrganizationEntityModel
async getIntegrationTenantSettings(
input: IIntegrationTenantFindInput
): Promise<IIntegrationTenant> {
try {
const { organizationId, tenantId } = options;
const tenantId = RequestContext.currentTenantId() || input.tenantId;
const { organizationId, name } = input;

return await this.findOneByOptions({
where: {
organizationId,
tenantId,
name: IntegrationEnum.GAUZY_AI
name,
isActive: true,
isArchived: false,
integration: {
provider: name,
isActive: true,
isArchived: false,
}
},
relations: {
settings: true
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
import { isNotEmpty } from '@gauzy/common';
import { IntegrationEnum } from '@gauzy/contracts';
import { RequestConfigProvider } from '@gauzy/integration-ai';
import { arrayToObject } from 'core/utils';
import { IntegrationTenantService } from 'integration-tenant/integration-tenant.service';
Expand Down Expand Up @@ -34,7 +35,11 @@ export class IntegrationAIMiddleware implements NestMiddleware {
// Check if tenant and organization IDs are not empty
if (isNotEmpty(tenantId) && isNotEmpty(organizationId)) {
// Fetch integration settings from the service
const { settings = [] } = await this.integrationTenantService.getIntegrationSettings({ tenantId, organizationId });
const { settings = [] } = await this.integrationTenantService.getIntegrationTenantSettings({
tenantId,
organizationId,
name: IntegrationEnum.GAUZY_AI
});
// Convert settings array to an object
const { apiKey: ApiKey, apiSecret: ApiSecret } = arrayToObject(settings, 'settingsName', 'settingsValue');

Expand Down
Loading

0 comments on commit 1e084ab

Please sign in to comment.