added yes or no data collection mode
This commit is contained in:
parent
52c9a42e42
commit
5bced9374b
|
|
@ -48,6 +48,7 @@ public class Installation : TreeNode
|
|||
public String ReadRoleId { get; set; } = "";
|
||||
public String WriteRoleId { get; set; } = "";
|
||||
public Boolean TestingMode { get; set; } = false;
|
||||
public Boolean DataCollectionEnabled { get; set; } = true;
|
||||
public int Status { get; set; } = -1;
|
||||
public int Product { get; set; } = (int)ProductType.Salimax;
|
||||
public int Device { get; set; } = 0;
|
||||
|
|
|
|||
|
|
@ -91,6 +91,11 @@ public static partial class Db
|
|||
Connection.Execute("UPDATE User SET Language = 'fr' WHERE Language = 'french'");
|
||||
Connection.Execute("UPDATE User SET Language = 'it' WHERE Language = 'italian'");
|
||||
|
||||
// Backfill: SQLite-net adds new bool columns as nullable with NULL for existing rows.
|
||||
// LINQ `.Where(i => i.DataCollectionEnabled)` translates to `WHERE ... = 1` and excludes
|
||||
// NULL rows, which would silently disable ingestion for every pre-existing installation.
|
||||
Connection.Execute("UPDATE Installation SET DataCollectionEnabled = 1 WHERE DataCollectionEnabled IS NULL");
|
||||
|
||||
// One-time migration: rebrand to inesco energy
|
||||
Connection.Execute("UPDATE Folder SET Name = 'inesco energy' WHERE Name = 'InnovEnergy'");
|
||||
Connection.Execute("UPDATE Folder SET Name = 'inesco energy' WHERE Name = 'inesco Energy'");
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ public static class DeleteOldDataFromS3
|
|||
{
|
||||
var cutoffTimestamp = DateTimeOffset.UtcNow.AddYears(-1).ToUnixTimeSeconds();
|
||||
var cutoffKey = cutoffTimestamp.ToString();
|
||||
var installations = Db.Installations.ToList();
|
||||
var installations = Db.Installations.Where(i => i.DataCollectionEnabled).ToList();
|
||||
|
||||
Console.WriteLine($"[S3Cleanup] Starting cleanup for {installations.Count} installations, cutoff: {cutoffKey}");
|
||||
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ public static class DailyIngestionService
|
|||
Console.WriteLine($"[DailyIngestion] Starting ingestion for all SodioHome installations...");
|
||||
|
||||
var installations = Db.Installations
|
||||
.Where(i => (i.Product == (Int32)ProductType.SodioHome || i.Product == (Int32)ProductType.SodistorePro) && i.Device != 3) // Skip Growatt (device=3)
|
||||
.Where(i => (i.Product == (Int32)ProductType.SodioHome || i.Product == (Int32)ProductType.SodistorePro) && i.Device != 3 && i.DataCollectionEnabled) // Skip Growatt (device=3) and installations with data collection disabled
|
||||
.ToList();
|
||||
|
||||
foreach (var installation in installations)
|
||||
|
|
@ -75,6 +75,13 @@ public static class DailyIngestionService
|
|||
/// </summary>
|
||||
public static async Task IngestInstallationAsync(Int64 installationId)
|
||||
{
|
||||
var installation = Db.GetInstallationById(installationId);
|
||||
if (installation is null || !installation.DataCollectionEnabled)
|
||||
{
|
||||
Console.WriteLine($"[DailyIngestion] Skipping installation {installationId} (data collection disabled).");
|
||||
return;
|
||||
}
|
||||
|
||||
await TryIngestFromJson(installationId);
|
||||
IngestFromXlsx(installationId);
|
||||
}
|
||||
|
|
@ -88,6 +95,11 @@ public static class DailyIngestionService
|
|||
{
|
||||
var installation = Db.GetInstallationById(installationId);
|
||||
if (installation is null) return;
|
||||
if (!installation.DataCollectionEnabled)
|
||||
{
|
||||
Console.WriteLine($"[DailyIngestion] Skipping date-range ingest for installation {installationId} (data collection disabled).");
|
||||
return;
|
||||
}
|
||||
|
||||
var newDaily = 0;
|
||||
var newHourly = 0;
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ public static class ReportAggregationService
|
|||
Console.WriteLine("[ReportAggregation] Running Monday weekly report generation...");
|
||||
|
||||
var installations = Db.Installations
|
||||
.Where(i => (i.Product == (Int32)ProductType.SodioHome || i.Product == (Int32)ProductType.SodistorePro) && i.Device != 3) // Skip Growatt (device=3)
|
||||
.Where(i => (i.Product == (Int32)ProductType.SodioHome || i.Product == (Int32)ProductType.SodistorePro) && i.Device != 3 && i.DataCollectionEnabled) // Skip Growatt (device=3) and installations with data collection disabled
|
||||
.ToList();
|
||||
|
||||
var generated = 0;
|
||||
|
|
|
|||
|
|
@ -762,6 +762,28 @@ function InformationSodistorehome(props: InformationSodistorehomeProps) {
|
|||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<FormControl fullWidth sx={{ marginLeft: 1, marginTop: 1, marginBottom: 1, width: 440 }}>
|
||||
<InputLabel sx={{ fontSize: 14, backgroundColor: 'white' }}>
|
||||
<FormattedMessage id="dataCollectionEnabled" defaultMessage="Data Collection" />
|
||||
</InputLabel>
|
||||
<Select
|
||||
name="dataCollectionEnabled"
|
||||
value={formValues.dataCollectionEnabled === false ? 'no' : 'yes'}
|
||||
onChange={(e) =>
|
||||
setFormValues({
|
||||
...formValues,
|
||||
dataCollectionEnabled: e.target.value === 'yes'
|
||||
})
|
||||
}
|
||||
inputProps={{ readOnly: !canEdit }}
|
||||
>
|
||||
<MenuItem value="yes"><FormattedMessage id="yes" defaultMessage="Yes" /></MenuItem>
|
||||
<MenuItem value="no"><FormattedMessage id="no" defaultMessage="No" /></MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<FormControl fullWidth sx={{ marginLeft: 1, marginTop: 1, marginBottom: 1, width: 440 }}>
|
||||
<InputLabel sx={{ fontSize: 14, backgroundColor: 'white' }}>
|
||||
|
|
|
|||
|
|
@ -209,46 +209,60 @@ const FlatInstallationView = (props: FlatInstallationViewProps) => {
|
|||
marginLeft: '15px'
|
||||
}}
|
||||
>
|
||||
{status === -1 ? (
|
||||
<CancelIcon
|
||||
{installation.dataCollectionEnabled === false ? (
|
||||
<div
|
||||
style={{
|
||||
width: '23px',
|
||||
height: '23px',
|
||||
color: 'red',
|
||||
borderRadius: '50%'
|
||||
width: '20px',
|
||||
height: '20px',
|
||||
marginLeft: '2px',
|
||||
borderRadius: '50%',
|
||||
backgroundColor: 'grey'
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
<>
|
||||
{status === -1 ? (
|
||||
<CancelIcon
|
||||
style={{
|
||||
width: '23px',
|
||||
height: '23px',
|
||||
color: 'red',
|
||||
borderRadius: '50%'
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
|
||||
{status === -2 ? (
|
||||
<CircularProgress
|
||||
size={20}
|
||||
sx={{
|
||||
color: '#f7b34d'
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
{status === -2 ? (
|
||||
<CircularProgress
|
||||
size={20}
|
||||
sx={{
|
||||
color: '#f7b34d'
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
|
||||
<div
|
||||
style={{
|
||||
width: '20px',
|
||||
height: '20px',
|
||||
marginLeft: '2px',
|
||||
borderRadius: '50%',
|
||||
backgroundColor:
|
||||
status === 2
|
||||
? 'red'
|
||||
: status === 1
|
||||
? 'orange'
|
||||
: status === -1 || status === -2
|
||||
? 'transparent'
|
||||
: 'green'
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
style={{
|
||||
width: '20px',
|
||||
height: '20px',
|
||||
marginLeft: '2px',
|
||||
borderRadius: '50%',
|
||||
backgroundColor:
|
||||
status === 2
|
||||
? 'red'
|
||||
: status === 1
|
||||
? 'orange'
|
||||
: status === -1 || status === -2
|
||||
? 'transparent'
|
||||
: 'green'
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{installation.testingMode && (
|
||||
<BuildIcon
|
||||
style={{
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ function SodioHomeInstallation(props: singleInstallationProps) {
|
|||
const [currentTab, setCurrentTab] = useState<string>(undefined);
|
||||
const [values, setValues] = useState<JSONRecordData | null>(null);
|
||||
const status = props.current_installation.status;
|
||||
const dataCollectionDisabled = props.current_installation.dataCollectionEnabled === false;
|
||||
const [
|
||||
failedToCommunicateWithInstallation,
|
||||
setFailedToCommunicateWithInstallation
|
||||
|
|
@ -417,46 +418,60 @@ function SodioHomeInstallation(props: singleInstallationProps) {
|
|||
marginTop: '-10px'
|
||||
}}
|
||||
>
|
||||
{status === -1 ? (
|
||||
<CancelIcon
|
||||
{dataCollectionDisabled ? (
|
||||
<div
|
||||
style={{
|
||||
width: '23px',
|
||||
height: '23px',
|
||||
color: 'red',
|
||||
borderRadius: '50%'
|
||||
width: '20px',
|
||||
height: '20px',
|
||||
marginLeft: '2px',
|
||||
borderRadius: '50%',
|
||||
backgroundColor: 'grey'
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
<>
|
||||
{status === -1 ? (
|
||||
<CancelIcon
|
||||
style={{
|
||||
width: '23px',
|
||||
height: '23px',
|
||||
color: 'red',
|
||||
borderRadius: '50%'
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
|
||||
{status === -2 ? (
|
||||
<CircularProgress
|
||||
size={20}
|
||||
sx={{
|
||||
color: '#f7b34d'
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
{status === -2 ? (
|
||||
<CircularProgress
|
||||
size={20}
|
||||
sx={{
|
||||
color: '#f7b34d'
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
|
||||
<div
|
||||
style={{
|
||||
width: '20px',
|
||||
height: '20px',
|
||||
marginLeft: '2px',
|
||||
borderRadius: '50%',
|
||||
backgroundColor:
|
||||
status === 2
|
||||
? 'red'
|
||||
: status === 1
|
||||
? 'orange'
|
||||
: status === -1 || status === -2
|
||||
? 'transparent'
|
||||
: 'green'
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
style={{
|
||||
width: '20px',
|
||||
height: '20px',
|
||||
marginLeft: '2px',
|
||||
borderRadius: '50%',
|
||||
backgroundColor:
|
||||
status === 2
|
||||
? 'red'
|
||||
: status === 1
|
||||
? 'orange'
|
||||
: status === -1 || status === -2
|
||||
? 'transparent'
|
||||
: 'green'
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
{props.current_installation.testingMode && (
|
||||
<BuildIcon
|
||||
|
|
@ -521,7 +536,7 @@ function SodioHomeInstallation(props: singleInstallationProps) {
|
|||
}
|
||||
/>
|
||||
|
||||
{currentUser.userType !== UserType.client && (
|
||||
{currentUser.userType !== UserType.client && !dataCollectionDisabled && (
|
||||
<Route
|
||||
path={routes.log}
|
||||
element={
|
||||
|
|
@ -534,19 +549,21 @@ function SodioHomeInstallation(props: singleInstallationProps) {
|
|||
/>
|
||||
)}
|
||||
|
||||
<Route
|
||||
path={routes.live}
|
||||
element={
|
||||
<TopologySodistoreHome
|
||||
values={values}
|
||||
connected={connected}
|
||||
loading={loading}
|
||||
batteryClusterNumber={props.current_installation.batteryClusterNumber}
|
||||
></TopologySodistoreHome>
|
||||
}
|
||||
/>
|
||||
{!dataCollectionDisabled && (
|
||||
<Route
|
||||
path={routes.live}
|
||||
element={
|
||||
<TopologySodistoreHome
|
||||
values={values}
|
||||
connected={connected}
|
||||
loading={loading}
|
||||
batteryClusterNumber={props.current_installation.batteryClusterNumber}
|
||||
></TopologySodistoreHome>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
|
||||
{currentUser.userType !== UserType.client && (
|
||||
{currentUser.userType !== UserType.client && !dataCollectionDisabled && (
|
||||
<Route
|
||||
path={routes.batteryview + '/*'}
|
||||
element={
|
||||
|
|
@ -573,7 +590,7 @@ function SodioHomeInstallation(props: singleInstallationProps) {
|
|||
/>
|
||||
)}
|
||||
|
||||
{currentUser.userType == UserType.admin && (
|
||||
{currentUser.userType == UserType.admin && !dataCollectionDisabled && (
|
||||
<Route
|
||||
path={routes.configuration}
|
||||
element={
|
||||
|
|
@ -600,21 +617,23 @@ function SodioHomeInstallation(props: singleInstallationProps) {
|
|||
/>
|
||||
)} */}
|
||||
|
||||
<Route
|
||||
path={routes.overview}
|
||||
element={
|
||||
<Overview
|
||||
s3Credentials={s3Credentials}
|
||||
id={props.current_installation.id}
|
||||
device={props.current_installation.device}
|
||||
product={props.current_installation.product}
|
||||
connected={connected}
|
||||
loading={loading}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
{!dataCollectionDisabled && (
|
||||
<Route
|
||||
path={routes.overview}
|
||||
element={
|
||||
<Overview
|
||||
s3Credentials={s3Credentials}
|
||||
id={props.current_installation.id}
|
||||
device={props.current_installation.device}
|
||||
product={props.current_installation.product}
|
||||
connected={connected}
|
||||
loading={loading}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
|
||||
{props.current_installation.device !== 3 && (
|
||||
{props.current_installation.device !== 3 && !dataCollectionDisabled && (
|
||||
<Route
|
||||
path={routes.report}
|
||||
element={
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ function SodistorehomeInstallationForm(props: SodistorehomeInstallationFormPros)
|
|||
vpnIp: '',
|
||||
installationModel: '',
|
||||
externalEms: 'No',
|
||||
dataCollectionEnabled: true,
|
||||
...(isSodistorePro ? { device: 4 } : {}),
|
||||
});
|
||||
const [inverterCount, setInverterCount] = useState('');
|
||||
|
|
@ -249,6 +250,46 @@ function SodistorehomeInstallationForm(props: SodistorehomeInstallationFormPros)
|
|||
</>
|
||||
)}
|
||||
|
||||
<div>
|
||||
<FormControl
|
||||
fullWidth
|
||||
sx={{
|
||||
marginTop: 1,
|
||||
marginBottom: 1,
|
||||
width: 390
|
||||
}}
|
||||
>
|
||||
<InputLabel
|
||||
sx={{
|
||||
fontSize: 14,
|
||||
backgroundColor: 'white'
|
||||
}}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="dataCollectionEnabled"
|
||||
defaultMessage="Data Collection"
|
||||
/>
|
||||
</InputLabel>
|
||||
<Select
|
||||
name="dataCollectionEnabled"
|
||||
value={formValues.dataCollectionEnabled ? 'yes' : 'no'}
|
||||
onChange={(e) =>
|
||||
setFormValues({
|
||||
...formValues,
|
||||
dataCollectionEnabled: e.target.value === 'yes'
|
||||
})
|
||||
}
|
||||
>
|
||||
<MenuItem value="yes">
|
||||
<FormattedMessage id="yes" defaultMessage="Yes" />
|
||||
</MenuItem>
|
||||
<MenuItem value="no">
|
||||
<FormattedMessage id="no" defaultMessage="No" />
|
||||
</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
</div>
|
||||
|
||||
</Box>
|
||||
<div
|
||||
style={{
|
||||
|
|
|
|||
|
|
@ -275,6 +275,12 @@ function SodioHomeInstallationTabs(props: SodioHomeInstallationTabsProps) {
|
|||
const isGrowatt = currentInstallation?.device === 3
|
||||
|| (installations.length === 1 && installations[0].device === 3);
|
||||
|
||||
// When data collection is disabled, only navigation, info, history, tickets, documents remain.
|
||||
const dataCollectionDisabled =
|
||||
currentInstallation?.dataCollectionEnabled === false
|
||||
|| (installations.length === 1 && installations[0].dataCollectionEnabled === false);
|
||||
const allowedWhenDisabled = ['list', 'tree', 'information', 'history', 'installationTickets', 'documents'];
|
||||
|
||||
const tabs = inInstallationView && currentUser.userType == UserType.admin
|
||||
? [
|
||||
{
|
||||
|
|
@ -471,6 +477,7 @@ function SodioHomeInstallationTabs(props: SodioHomeInstallationTabsProps) {
|
|||
>
|
||||
{tabs
|
||||
.filter((tab) => !(isGrowatt && tab.value === 'report'))
|
||||
.filter((tab) => !dataCollectionDisabled || allowedWhenDisabled.includes(tab.value))
|
||||
.map((tab) => (
|
||||
<Tab
|
||||
key={tab.value}
|
||||
|
|
@ -544,6 +551,7 @@ function SodioHomeInstallationTabs(props: SodioHomeInstallationTabsProps) {
|
|||
>
|
||||
{singleInstallationTabs
|
||||
.filter((tab) => !(isGrowatt && tab.value === 'report'))
|
||||
.filter((tab) => !dataCollectionDisabled || allowedWhenDisabled.includes(tab.value))
|
||||
.map((tab) => (
|
||||
<Tab
|
||||
key={tab.value}
|
||||
|
|
|
|||
|
|
@ -160,50 +160,64 @@ function CustomTreeItem(props: CustomTreeItemProps) {
|
|||
|
||||
{props.node.type === 'Installation' && (
|
||||
<div>
|
||||
{status === -1 ? (
|
||||
<CancelIcon
|
||||
{(props.node as any).dataCollectionEnabled === false ? (
|
||||
<div
|
||||
style={{
|
||||
width: '23px',
|
||||
height: '23px',
|
||||
color: 'red',
|
||||
width: '20px',
|
||||
height: '20px',
|
||||
borderRadius: '50%',
|
||||
marginLeft: '30px',
|
||||
marginTop: '30px'
|
||||
marginLeft: '17px',
|
||||
backgroundColor: 'grey'
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
<>
|
||||
{status === -1 ? (
|
||||
<CancelIcon
|
||||
style={{
|
||||
width: '23px',
|
||||
height: '23px',
|
||||
color: 'red',
|
||||
borderRadius: '50%',
|
||||
marginLeft: '30px',
|
||||
marginTop: '30px'
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
|
||||
{status === -2 ? (
|
||||
<CircularProgress
|
||||
size={20}
|
||||
sx={{
|
||||
color: '#f7b34d',
|
||||
marginLeft: '22px',
|
||||
marginTop: '30px'
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
{status === -2 ? (
|
||||
<CircularProgress
|
||||
size={20}
|
||||
sx={{
|
||||
color: '#f7b34d',
|
||||
marginLeft: '22px',
|
||||
marginTop: '30px'
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
|
||||
<div
|
||||
style={{
|
||||
width: '20px',
|
||||
height: '20px',
|
||||
borderRadius: '50%',
|
||||
marginLeft: '17px',
|
||||
backgroundColor:
|
||||
status === 2
|
||||
? 'red'
|
||||
: status === 1
|
||||
? 'orange'
|
||||
: status === -1 || status === -2
|
||||
? 'transparent'
|
||||
: 'green'
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
style={{
|
||||
width: '20px',
|
||||
height: '20px',
|
||||
borderRadius: '50%',
|
||||
marginLeft: '17px',
|
||||
backgroundColor:
|
||||
status === 2
|
||||
? 'red'
|
||||
: status === 1
|
||||
? 'orange'
|
||||
: status === -1 || status === -2
|
||||
? 'transparent'
|
||||
: 'green'
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ export interface I_Installation extends I_S3Credentials {
|
|||
product: number;
|
||||
device: number;
|
||||
testingMode?: boolean;
|
||||
dataCollectionEnabled?: boolean;
|
||||
status?: number;
|
||||
serialNumber?: string;
|
||||
networkProvider: string;
|
||||
|
|
|
|||
|
|
@ -86,6 +86,9 @@
|
|||
"externalEmsOther": "Externes EMS (angeben)",
|
||||
"emsNo": "Nein",
|
||||
"emsOther": "Andere",
|
||||
"yes": "Ja",
|
||||
"no": "Nein",
|
||||
"dataCollectionEnabled": "Datenerfassung",
|
||||
"generalInfo": "Allgemeine Informationen",
|
||||
"installationSetup": "Installationseinrichtung",
|
||||
"couplingType": "AC/DC-Kopplung",
|
||||
|
|
|
|||
|
|
@ -68,6 +68,9 @@
|
|||
"externalEmsOther": "External EMS (specify)",
|
||||
"emsNo": "No",
|
||||
"emsOther": "Other",
|
||||
"yes": "Yes",
|
||||
"no": "No",
|
||||
"dataCollectionEnabled": "Data Collection",
|
||||
"generalInfo": "General Info",
|
||||
"installationSetup": "Installation Setup",
|
||||
"couplingType": "AC/DC Coupling",
|
||||
|
|
|
|||
|
|
@ -80,6 +80,9 @@
|
|||
"externalEmsOther": "EMS externe (préciser)",
|
||||
"emsNo": "Non",
|
||||
"emsOther": "Autre",
|
||||
"yes": "Oui",
|
||||
"no": "Non",
|
||||
"dataCollectionEnabled": "Collecte de données",
|
||||
"generalInfo": "Informations générales",
|
||||
"installationSetup": "Configuration de l'installation",
|
||||
"couplingType": "Couplage AC/DC",
|
||||
|
|
|
|||
|
|
@ -68,6 +68,9 @@
|
|||
"externalEmsOther": "EMS esterno (specificare)",
|
||||
"emsNo": "No",
|
||||
"emsOther": "Altro",
|
||||
"yes": "Sì",
|
||||
"no": "No",
|
||||
"dataCollectionEnabled": "Raccolta dati",
|
||||
"generalInfo": "Informazioni generali",
|
||||
"installationSetup": "Configurazione installazione",
|
||||
"couplingType": "Accoppiamento AC/DC",
|
||||
|
|
|
|||
Loading…
Reference in New Issue