Abdul Rehman
commited on
Commit
·
644d830
1
Parent(s):
d60fe6f
tracking code
Browse files- src/app.module.ts +2 -5
- src/constants/collections.constant.ts +1 -0
- src/constants/repository.constant.ts +1 -0
- src/modules/activity/activity.controller.ts +145 -0
- src/modules/activity/activity.module.ts +13 -0
- src/modules/activity/activity.provider.ts +12 -0
- src/modules/activity/activity.schema.ts +35 -0
- src/modules/activity/activity.service.ts +23 -0
- src/modules/database/database.module.ts +13 -2
- src/modules/property/property.schema.ts +4 -0
src/app.module.ts
CHANGED
@@ -4,13 +4,10 @@ import { AppService } from './app.service';
|
|
4 |
import { PropertyModule } from './modules/property/property.module';
|
5 |
import { AuthModule } from './modules/auth/auth.module';
|
6 |
import { UserModule } from './modules/user/user.module';
|
|
|
7 |
|
8 |
@Module({
|
9 |
-
imports: [
|
10 |
-
PropertyModule,
|
11 |
-
AuthModule,
|
12 |
-
UserModule,
|
13 |
-
],
|
14 |
controllers: [AppController],
|
15 |
providers: [AppService],
|
16 |
})
|
|
|
4 |
import { PropertyModule } from './modules/property/property.module';
|
5 |
import { AuthModule } from './modules/auth/auth.module';
|
6 |
import { UserModule } from './modules/user/user.module';
|
7 |
+
import { ActivityModule } from './modules/activity/activity.module';
|
8 |
|
9 |
@Module({
|
10 |
+
imports: [PropertyModule, AuthModule, UserModule, ActivityModule],
|
|
|
|
|
|
|
|
|
11 |
controllers: [AppController],
|
12 |
providers: [AppService],
|
13 |
})
|
src/constants/collections.constant.ts
CHANGED
@@ -1,2 +1,3 @@
|
|
1 |
export const PROPERTIE = 'propertie';
|
2 |
export const USERS = 'users';
|
|
|
|
1 |
export const PROPERTIE = 'propertie';
|
2 |
export const USERS = 'users';
|
3 |
+
export const ACTIVITY = 'activity';
|
src/constants/repository.constant.ts
CHANGED
@@ -1,2 +1,3 @@
|
|
1 |
export const PROPERTY_REPOSITORY = 'PROPERTY_REPOSITORY';
|
2 |
export const USER_REPOSITORY = 'USER_REPOSITORY';
|
|
|
|
1 |
export const PROPERTY_REPOSITORY = 'PROPERTY_REPOSITORY';
|
2 |
export const USER_REPOSITORY = 'USER_REPOSITORY';
|
3 |
+
export const ACTIVITY_REPOSITORY = 'ACTIVITY_REPOSITORY';
|
src/modules/activity/activity.controller.ts
ADDED
@@ -0,0 +1,145 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import {
|
2 |
+
Body,
|
3 |
+
Controller,
|
4 |
+
Get,
|
5 |
+
HttpStatus,
|
6 |
+
Patch,
|
7 |
+
Post,
|
8 |
+
Query,
|
9 |
+
Req,
|
10 |
+
Res,
|
11 |
+
UseGuards,
|
12 |
+
} from '@nestjs/common';
|
13 |
+
import { Response } from 'express';
|
14 |
+
import { CommonServices } from '../shared/common.service';
|
15 |
+
import { ActivityService } from './activity.service';
|
16 |
+
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
|
17 |
+
import { PropertyService } from '../property/property.service';
|
18 |
+
|
19 |
+
@Controller('activity')
|
20 |
+
export class ActivityController extends CommonServices {
|
21 |
+
constructor(
|
22 |
+
private readonly activityService: ActivityService,
|
23 |
+
private readonly propertyService: PropertyService,
|
24 |
+
) {
|
25 |
+
super();
|
26 |
+
}
|
27 |
+
|
28 |
+
@Post('create')
|
29 |
+
@UseGuards(JwtAuthGuard)
|
30 |
+
async createActivity(@Body() body: any, @Res() res: Response, @Req() req) {
|
31 |
+
try {
|
32 |
+
const payload = {
|
33 |
+
...body,
|
34 |
+
userId: req.user.userId,
|
35 |
+
};
|
36 |
+
const activity = await this.activityService.sharedCreate(payload);
|
37 |
+
|
38 |
+
// update views
|
39 |
+
if (body.action == 'view')
|
40 |
+
await this.propertyService.sharedFindOneAndUpdate(
|
41 |
+
{ _id: body.propertyId },
|
42 |
+
{
|
43 |
+
$inc: { views: 1 }, // Increment views and add duration
|
44 |
+
},
|
45 |
+
{},
|
46 |
+
);
|
47 |
+
|
48 |
+
//update duration/ time spend
|
49 |
+
if (body.action == 'time_spent')
|
50 |
+
await this.propertyService.sharedFindOneAndUpdate(
|
51 |
+
{ _id: body.propertyId },
|
52 |
+
{
|
53 |
+
$inc: { total_time_spent: body.duration || 0 }, // Increment views and add duration
|
54 |
+
},
|
55 |
+
{},
|
56 |
+
);
|
57 |
+
|
58 |
+
return this.sendResponse(
|
59 |
+
this.messages.Success,
|
60 |
+
activity,
|
61 |
+
HttpStatus.OK,
|
62 |
+
res,
|
63 |
+
);
|
64 |
+
} catch (error) {
|
65 |
+
console.log(error);
|
66 |
+
return this.sendResponse(
|
67 |
+
'Error',
|
68 |
+
{},
|
69 |
+
HttpStatus.INTERNAL_SERVER_ERROR,
|
70 |
+
res,
|
71 |
+
);
|
72 |
+
}
|
73 |
+
}
|
74 |
+
|
75 |
+
@Get('')
|
76 |
+
async getActivityListings(@Res() res: Response, @Req() req): Promise<any> {
|
77 |
+
try {
|
78 |
+
const response = await this.activityService.sharedFind({});
|
79 |
+
return this.sendResponse(
|
80 |
+
this.messages.Success,
|
81 |
+
response,
|
82 |
+
HttpStatus.OK,
|
83 |
+
res,
|
84 |
+
);
|
85 |
+
} catch (error) {
|
86 |
+
return this.sendResponse(
|
87 |
+
'Internal server Error',
|
88 |
+
{},
|
89 |
+
HttpStatus.INTERNAL_SERVER_ERROR,
|
90 |
+
res,
|
91 |
+
);
|
92 |
+
}
|
93 |
+
}
|
94 |
+
|
95 |
+
// New Update API to modify existing activity
|
96 |
+
@Patch('update')
|
97 |
+
@UseGuards(JwtAuthGuard)
|
98 |
+
async updateActivity(
|
99 |
+
@Body() body: any, // should include { userId, propertyId, action, duration (optional) }
|
100 |
+
@Res() res: Response,
|
101 |
+
@Req() req,
|
102 |
+
) {
|
103 |
+
try {
|
104 |
+
// Check if the activity exists for the user and property
|
105 |
+
const existingActivity = await this.activityService.sharedFindOne({
|
106 |
+
userId: body.userId,
|
107 |
+
propertyId: body.propertyId,
|
108 |
+
action: body.action, // Example: 'click' or 'view'
|
109 |
+
});
|
110 |
+
|
111 |
+
if (existingActivity) {
|
112 |
+
// If activity exists, update it
|
113 |
+
const updatedActivity = await this.activityService.sharedUpdate(
|
114 |
+
{ _id: existingActivity._id },
|
115 |
+
{
|
116 |
+
duration: body.duration ? body.duration : existingActivity.duration,
|
117 |
+
timestamp: new Date(), // update the timestamp to the latest interaction
|
118 |
+
},
|
119 |
+
);
|
120 |
+
|
121 |
+
return this.sendResponse(
|
122 |
+
this.messages.Success,
|
123 |
+
updatedActivity,
|
124 |
+
HttpStatus.OK,
|
125 |
+
res,
|
126 |
+
);
|
127 |
+
} else {
|
128 |
+
// If no activity exists, return a not found response
|
129 |
+
return this.sendResponse(
|
130 |
+
'Activity not found',
|
131 |
+
{},
|
132 |
+
HttpStatus.NOT_FOUND,
|
133 |
+
res,
|
134 |
+
);
|
135 |
+
}
|
136 |
+
} catch (error) {
|
137 |
+
return this.sendResponse(
|
138 |
+
'Internal server Error',
|
139 |
+
{},
|
140 |
+
HttpStatus.INTERNAL_SERVER_ERROR,
|
141 |
+
res,
|
142 |
+
);
|
143 |
+
}
|
144 |
+
}
|
145 |
+
}
|
src/modules/activity/activity.module.ts
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Module } from '@nestjs/common';
|
2 |
+
import { DatabaseModule } from '../database/database.module';
|
3 |
+
import { ActivityController } from './activity.controller';
|
4 |
+
import { ActivityService } from './activity.service';
|
5 |
+
import { PropertyService } from '../property/property.service';
|
6 |
+
|
7 |
+
@Module({
|
8 |
+
imports: [DatabaseModule],
|
9 |
+
controllers: [ActivityController],
|
10 |
+
providers: [ActivityService, PropertyService],
|
11 |
+
exports: [ActivityService, PropertyService],
|
12 |
+
})
|
13 |
+
export class ActivityModule {}
|
src/modules/activity/activity.provider.ts
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Connection } from 'mongoose';
|
2 |
+
import { ACTIVITY, ACTIVITY_REPOSITORY } from 'src/constants';
|
3 |
+
import { UserActivitySchema } from './activity.schema';
|
4 |
+
|
5 |
+
export const activityProviders = [
|
6 |
+
{
|
7 |
+
provide: ACTIVITY_REPOSITORY,
|
8 |
+
useFactory: (connection: Connection) =>
|
9 |
+
connection.model(ACTIVITY, UserActivitySchema),
|
10 |
+
inject: ['DATABASE_CONNECTION'],
|
11 |
+
},
|
12 |
+
];
|
src/modules/activity/activity.schema.ts
ADDED
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import * as mongoose from 'mongoose';
|
2 |
+
import { Document } from 'mongoose';
|
3 |
+
import { IUserDocument } from '../user/user.schema';
|
4 |
+
import { IPropertyDocument } from '../property/property.schema';
|
5 |
+
|
6 |
+
export interface IUserActivityDocument extends Document {
|
7 |
+
userId: IUserDocument;
|
8 |
+
propertyId: IPropertyDocument;
|
9 |
+
action: string; // E.g., 'click', 'view', 'time_spent'
|
10 |
+
timestamp: Date;
|
11 |
+
duration?: number; // Store time spent in seconds for 'time_spent' action
|
12 |
+
}
|
13 |
+
|
14 |
+
const UserActivitySchema = new mongoose.Schema<IUserActivityDocument>(
|
15 |
+
{
|
16 |
+
userId: {
|
17 |
+
type: mongoose.Schema.Types.ObjectId,
|
18 |
+
ref: 'User',
|
19 |
+
required: true,
|
20 |
+
},
|
21 |
+
propertyId: {
|
22 |
+
type: mongoose.Schema.Types.ObjectId,
|
23 |
+
ref: 'propertie',
|
24 |
+
required: true,
|
25 |
+
},
|
26 |
+
action: { type: String, required: true }, // 'click', 'view', 'time_spent'
|
27 |
+
timestamp: { type: Date, default: Date.now },
|
28 |
+
duration: { type: Number }, // Only applicable for 'time_spent'
|
29 |
+
},
|
30 |
+
{
|
31 |
+
toJSON: { versionKey: false },
|
32 |
+
},
|
33 |
+
);
|
34 |
+
|
35 |
+
export { UserActivitySchema };
|
src/modules/activity/activity.service.ts
ADDED
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Inject, Injectable } from '@nestjs/common';
|
2 |
+
import { Model, Types } from 'mongoose';
|
3 |
+
import { sharedCrudService } from '../shared/sharedCrud.services';
|
4 |
+
import { ACTIVITY_REPOSITORY } from 'src/constants';
|
5 |
+
import { IUserActivityDocument } from './activity.schema';
|
6 |
+
|
7 |
+
@Injectable()
|
8 |
+
export class ActivityService extends sharedCrudService {
|
9 |
+
constructor(
|
10 |
+
@Inject(ACTIVITY_REPOSITORY)
|
11 |
+
readonly activityRepository: Model<IUserActivityDocument>,
|
12 |
+
) {
|
13 |
+
super(activityRepository);
|
14 |
+
}
|
15 |
+
|
16 |
+
async activityLisitng(
|
17 |
+
page: number,
|
18 |
+
resPerPage: number,
|
19 |
+
search: string,
|
20 |
+
): Promise<any> {
|
21 |
+
return 'Hello from Activity service.'
|
22 |
+
}
|
23 |
+
}
|
src/modules/database/database.module.ts
CHANGED
@@ -2,9 +2,20 @@ import { Module } from '@nestjs/common';
|
|
2 |
import { databaseProviders } from './database.provider';
|
3 |
import { propertyProviders } from '../property/property.provider';
|
4 |
import { usersProviders } from '../user/user.provider';
|
|
|
5 |
|
6 |
@Module({
|
7 |
-
providers: [
|
8 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
9 |
})
|
10 |
export class DatabaseModule {}
|
|
|
2 |
import { databaseProviders } from './database.provider';
|
3 |
import { propertyProviders } from '../property/property.provider';
|
4 |
import { usersProviders } from '../user/user.provider';
|
5 |
+
import { activityProviders } from '../activity/activity.provider';
|
6 |
|
7 |
@Module({
|
8 |
+
providers: [
|
9 |
+
...databaseProviders,
|
10 |
+
...propertyProviders,
|
11 |
+
...usersProviders,
|
12 |
+
...activityProviders,
|
13 |
+
],
|
14 |
+
exports: [
|
15 |
+
...databaseProviders,
|
16 |
+
...propertyProviders,
|
17 |
+
...usersProviders,
|
18 |
+
...activityProviders,
|
19 |
+
],
|
20 |
})
|
21 |
export class DatabaseModule {}
|
src/modules/property/property.schema.ts
CHANGED
@@ -10,6 +10,8 @@ export interface IPropertyDocument extends Document {
|
|
10 |
features?: object;
|
11 |
thumbnail_summary?: string;
|
12 |
listing_url?: string;
|
|
|
|
|
13 |
createdAt?: Date;
|
14 |
updatedAt?: Date;
|
15 |
}
|
@@ -23,6 +25,8 @@ const PropertySchema = new mongoose.Schema<IPropertyDocument>(
|
|
23 |
features: { type: Object },
|
24 |
listing_url: { type: String },
|
25 |
imgs: [],
|
|
|
|
|
26 |
createdAt: { type: Date, default: Date.now },
|
27 |
updatedAt: { type: Date, default: Date.now },
|
28 |
},
|
|
|
10 |
features?: object;
|
11 |
thumbnail_summary?: string;
|
12 |
listing_url?: string;
|
13 |
+
views?: number; // Track the number of views
|
14 |
+
total_time_spent?: number; // Track total time spent on the property in seconds
|
15 |
createdAt?: Date;
|
16 |
updatedAt?: Date;
|
17 |
}
|
|
|
25 |
features: { type: Object },
|
26 |
listing_url: { type: String },
|
27 |
imgs: [],
|
28 |
+
views: { type: Number, default: 0 }, // New field to store the number of views
|
29 |
+
total_time_spent: { type: Number, default: 0 }, // New field to store total time spent in seconds
|
30 |
createdAt: { type: Date, default: Date.now },
|
31 |
updatedAt: { type: Date, default: Date.now },
|
32 |
},
|