CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
sagemathinc

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/next/components/statistics/opened-files.tsx
Views: 687
1
/*
2
* This file is part of CoCalc: Copyright © 2021 Sagemath, Inc.
3
* License: MS-RSL – see LICENSE.md for details
4
*/
5
6
import { Switch, Table } from "antd";
7
import { useMemo, useState } from "react";
8
9
import { Icon } from "@cocalc/frontend/components/icon";
10
import { file_associations } from "@cocalc/frontend/file-associations";
11
import { cmp, field_cmp } from "@cocalc/util/cmp";
12
import { Stats } from "@cocalc/util/db-schema/stats";
13
import { Paragraph, Title } from "components/misc";
14
import A from "components/misc/A";
15
16
const openedFilesColumns = [
17
{
18
title: "Type of File",
19
dataIndex: "ext",
20
key: "ext",
21
render: (ext) => {
22
const icon = (
23
<Icon
24
style={{ marginRight: "10px", fontSize: "14pt" }}
25
name={file_associations[ext]?.icon ?? file_associations[""]?.icon}
26
/>
27
);
28
const info = extensionToInfo[ext];
29
if (info == null)
30
return (
31
<>
32
{icon} {ext}
33
</>
34
);
35
const x = (
36
<>
37
{icon} {info.desc} (.{ext})
38
</>
39
);
40
if (info.link == null) return x;
41
return <A href={info.link}>{x}</A>;
42
},
43
sorter: (a, b) =>
44
cmp(
45
extensionToInfo[a.ext]?.desc ?? a.ext,
46
extensionToInfo[b.ext]?.desc ?? b.ext,
47
),
48
},
49
{
50
title: "Hour",
51
dataIndex: "1h",
52
key: "1h",
53
sorter: field_cmp("1h"),
54
defaultSortOrder: "descend" as any,
55
},
56
{ title: "Day", dataIndex: "1d", key: "1d", sorter: field_cmp("1d") },
57
{ title: "Week", dataIndex: "7d", key: "7d", sorter: field_cmp("7d") },
58
{
59
title: "Month",
60
dataIndex: "30d",
61
key: "30d",
62
sorter: field_cmp("30d"),
63
},
64
];
65
66
const extensionToInfo: { [ext: string]: { desc: string; link?: string } } = {
67
md: { desc: "Markdown" },
68
py: { desc: "Python", link: "/features/python" },
69
jpg: { desc: "Image" },
70
pdf: { desc: "PDF" },
71
png: { desc: "Image" },
72
rmd: { desc: "RMarkdown", link: "/features/r" },
73
rnw: { desc: "Knitr" },
74
rst: { desc: "ReST" },
75
svg: { desc: "Image" },
76
tex: { desc: "LaTeX", link: "/features/latex-editor" },
77
txt: { desc: "Plain Text" },
78
x11: { desc: "X11 Linux Desktop", link: "/features/x11" },
79
jpeg: { desc: "Image" },
80
lean: { desc: "LEAN theorem prover" },
81
rtex: { desc: "Knitr" },
82
sage: {
83
desc: "SageMath",
84
link: "https://www.sagemath.org/",
85
},
86
term: { desc: "Linux Terminal", link: "/features/terminal" },
87
ipynb: { desc: "Jupyter Notebook", link: "/features/jupyter-notebook" },
88
tasks: { desc: "Task List", link: "https://doc.cocalc.com/tasks.html" },
89
course: {
90
desc: "Course Management",
91
link: "https://doc.cocalc.com/teaching-instructors.html",
92
},
93
sagews: {
94
desc: "Sage Worksheet",
95
link: "https://doc.cocalc.com/sagews.html",
96
},
97
"sage-chat": { desc: "Chatroom" },
98
board: { desc: "Whiteboard", link: "/features/whiteboard" },
99
slides: { desc: "Slides", link: "/features/slides" },
100
} as const;
101
102
function processFilesOpened(
103
filesOpened,
104
distinct: boolean,
105
): {
106
rows: {
107
ext: string;
108
"1h": number;
109
"1d": number;
110
"7d": number;
111
"30d": number;
112
}[];
113
lastHour: number;
114
} {
115
let lastHour = 0;
116
const counts = distinct ? filesOpened.distinct : filesOpened.total;
117
const byExtension: {
118
[ext: string]: {
119
"1h": number;
120
"1d": number;
121
"7d": number;
122
"30d": number;
123
};
124
} = {};
125
126
for (const time in counts) {
127
const extToCount = counts[time];
128
for (const ext in extToCount) {
129
const cnt = parseInt(extToCount[ext]);
130
if (byExtension[ext] == null) {
131
byExtension[ext] = { "1h": 0, "1d": 0, "7d": 0, "30d": 0 };
132
}
133
byExtension[ext][time] += cnt;
134
if (time == "1h") {
135
lastHour += cnt;
136
}
137
}
138
}
139
140
const rows: {
141
ext: string;
142
"1h": number;
143
"1d": number;
144
"7d": number;
145
"30d": number;
146
}[] = [];
147
for (const ext in byExtension) {
148
const counts = byExtension[ext];
149
rows.push({ ext, ...counts });
150
}
151
return { rows, lastHour };
152
}
153
154
export default function OpenedFiles({
155
filesOpened,
156
style,
157
}: {
158
filesOpened: Stats["files_opened"];
159
style?: React.CSSProperties;
160
}) {
161
const [distinct, setDistinct] = useState<boolean>(true);
162
const { rows, lastHour } = useMemo(
163
() => processFilesOpened(filesOpened, distinct),
164
[distinct, filesOpened],
165
);
166
167
return (
168
<div style={style}>
169
<Paragraph style={{ float: "right" }}>
170
<Switch checked={distinct} onChange={setDistinct} /> Distinct
171
</Paragraph>
172
<Title level={2}>
173
{distinct ? "Distinct " : ""}Files Used in the Last Hour: {lastHour}{" "}
174
</Title>
175
<Paragraph>
176
Track the number of {distinct ? "distinct" : ""} files of each type that
177
people opened during the last hour, day, week and month.
178
</Paragraph>
179
<Table
180
dataSource={rows}
181
columns={openedFilesColumns}
182
bordered
183
pagination={false}
184
rowKey={"ext"}
185
/>
186
</div>
187
);
188
}
189
190